1 /* 2 * %CopyrightBegin% 3 * 4 * Copyright Ericsson AB 2000-2016. All Rights Reserved. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * %CopyrightEnd% 19 */ 20 package com.ericsson.otp.erlang; 21 22 /** 23 * Provides a Java representation of Erlang refs. There are two styles of Erlang 24 * refs, old style (one id value) and new style (array of id values). This class 25 * manages both types. 26 */ 27 public class OtpErlangRef extends OtpErlangObject { 28 // don't change this! 29 private static final long serialVersionUID = -7022666480768586521L; 30 31 private final String node; 32 private final int creation; 33 34 // old style refs have one 18-bit id 35 // r6 "new" refs have array of ids, first one is only 18 bits however 36 // 19 "newer" refs have full 32-bits for creation and for ids[0] 37 private int ids[] = null; 38 39 /** 40 * Create a unique Erlang ref belonging to the local node. 41 * 42 * @param self 43 * the local node. 44 * 45 * @deprecated use OtpLocalNode:createRef() instead 46 */ 47 @Deprecated OtpErlangRef(final OtpLocalNode self)48 public OtpErlangRef(final OtpLocalNode self) { 49 final OtpErlangRef r = self.createRef(); 50 51 ids = r.ids; 52 creation = r.creation; 53 node = r.node; 54 } 55 56 /** 57 * Create an Erlang ref from a stream containing a ref encoded in Erlang 58 * external format. 59 * 60 * @param buf 61 * the stream containing the encoded ref. 62 * 63 * @exception OtpErlangDecodeException 64 * if the buffer does not contain a valid external 65 * representation of an Erlang ref. 66 */ OtpErlangRef(final OtpInputStream buf)67 public OtpErlangRef(final OtpInputStream buf) 68 throws OtpErlangDecodeException { 69 final OtpErlangRef r = buf.read_ref(); 70 71 node = r.node(); 72 creation = r.creation(); 73 74 ids = r.ids(); 75 } 76 77 /** 78 * Create an old style Erlang ref from its components. 79 * 80 * @param node 81 * the nodename. 82 * 83 * @param id 84 * an arbitrary number. Only the low order 18 bits will be used. 85 * 86 * @param creation 87 * another arbitrary number. 88 */ OtpErlangRef(final String node, final int id, final int creation)89 public OtpErlangRef(final String node, final int id, final int creation) { 90 this.node = node; 91 ids = new int[1]; 92 ids[0] = id & 0x3ffff; // 18 bits 93 this.creation = creation & 0x03; // 2 bits 94 } 95 96 /** 97 * Create a new style Erlang ref from its components. 98 * 99 * @param node 100 * the nodename. 101 * 102 * @param ids 103 * an array of arbitrary numbers. Only the low order 18 bits of 104 * the first number will be used. If the array contains only one 105 * number, an old style ref will be written instead. At most 106 * three numbers will be read from the array. 107 * 108 * @param creation 109 * another arbitrary number. Only the low order 2 bits will be 110 * used. 111 */ OtpErlangRef(final String node, final int[] ids, final int creation)112 public OtpErlangRef(final String node, final int[] ids, final int creation) { 113 this(OtpExternal.newRefTag, node, ids, creation); 114 } 115 116 /** 117 * Create a new(er) style Erlang ref from its components. 118 * 119 * @param tag 120 * the external format to be compliant with. 121 * OtpExternal.newRefTag where only a subset of the bits are used (see other constructor) 122 * OtpExternal.newerRefTag where all bits of ids and creation are used. 123 * newerPortTag can only be decoded by OTP-19 and newer. 124 * 125 * @param node 126 * the nodename. 127 * 128 * @param ids 129 * an array of arbitrary numbers. At most three numbers 130 * will be read from the array. 131 * 132 * @param creation 133 * another arbitrary number. 134 */ OtpErlangRef(final int tag, final String node, final int[] ids, final int creation)135 public OtpErlangRef(final int tag, final String node, final int[] ids, 136 final int creation) { 137 this.node = node; 138 139 // use at most 5 words 140 int len = ids.length; 141 if (len < 3) { 142 this.ids = new int[3]; 143 this.ids[0] = 0; 144 this.ids[1] = 0; 145 this.ids[2] = 0; 146 } 147 else if (len <= 5) { 148 this.ids = new int[len]; 149 } 150 else { 151 this.ids = new int[5]; 152 len = 5; 153 } 154 System.arraycopy(ids, 0, this.ids, 0, len); 155 if (tag == OtpExternal.newRefTag) { 156 this.creation = creation & 0x3; 157 this.ids[0] &= 0x3ffff; // only 18 significant bits in first number 158 } 159 else { 160 this.creation = creation; 161 } 162 } 163 tag()164 protected int tag() { 165 return OtpExternal.newerRefTag; 166 } 167 168 /** 169 * Get the id number from the ref. Old style refs have only one id number. 170 * If this is a new style ref, the first id number is returned. 171 * 172 * @return the id number from the ref. 173 */ id()174 public int id() { 175 return ids[0]; 176 } 177 178 /** 179 * Get the array of id numbers from the ref. If this is an old style ref, 180 * the array is of length 1. If this is a new style ref, the array has 181 * length 3-5. 182 * 183 * @return the array of id numbers from the ref. 184 */ ids()185 public int[] ids() { 186 return ids; 187 } 188 189 /** 190 * Determine whether this is a new style ref. 191 * 192 * @return true if this ref is a new style ref, false otherwise. 193 */ isNewRef()194 public boolean isNewRef() { 195 return ids.length > 1; 196 } 197 198 /** 199 * Get the creation number from the ref. 200 * 201 * @return the creation number from the ref. 202 */ creation()203 public int creation() { 204 return creation; 205 } 206 207 /** 208 * Get the node name from the ref. 209 * 210 * @return the node name from the ref. 211 */ node()212 public String node() { 213 return node; 214 } 215 216 /** 217 * Get the string representation of the ref. Erlang refs are printed as 218 * #Ref<node.id> 219 * 220 * @return the string representation of the ref. 221 */ 222 @Override toString()223 public String toString() { 224 String s = "#Ref<" + node; 225 226 for (int i = 0; i < ids.length; i++) { 227 s += "." + ids[i]; 228 } 229 230 s += ">"; 231 232 return s; 233 } 234 235 /** 236 * Convert this ref to the equivalent Erlang external representation. 237 * 238 * @param buf 239 * an output stream to which the encoded ref should be written. 240 */ 241 @Override encode(final OtpOutputStream buf)242 public void encode(final OtpOutputStream buf) { 243 buf.write_ref(this); 244 } 245 246 /** 247 * Determine if two refs are equal. Refs are equal if their components are 248 * equal. New refs and old refs are considered equal if the node, creation 249 * and first id numnber are equal. 250 * 251 * @param o 252 * the other ref to compare to. 253 * 254 * @return true if the refs are equal, false otherwise. 255 */ 256 @Override equals(final Object o)257 public boolean equals(final Object o) { 258 if (!(o instanceof OtpErlangRef)) { 259 return false; 260 } 261 262 final OtpErlangRef ref = (OtpErlangRef) o; 263 264 if (!(node.equals(ref.node()) && creation == ref.creation())) { 265 return false; 266 } 267 268 if (ids.length != ref.ids.length) { 269 if (ids.length > ref.ids.length) { 270 for (int i = ref.ids.length; i < ids.length; i++) { 271 if (ids[i] != 0) { 272 return false; 273 } 274 } 275 } 276 else { 277 for (int i = ids.length; i < ref.ids.length; i++) { 278 if (ref.ids[i] != 0) { 279 return false; 280 } 281 } 282 } 283 } 284 for (int i = 0; i < ids.length; i++) { 285 if (ids[i] != ref.ids[i]) { 286 return false; 287 } 288 } 289 return true; 290 } 291 292 /** 293 * Compute the hashCode value for a given ref. This function is compatible 294 * with equal. 295 * 296 * @return the hashCode of the node. 297 **/ 298 299 @Override doHashCode()300 protected int doHashCode() { 301 final OtpErlangObject.Hash hash = new OtpErlangObject.Hash(7); 302 hash.combine(creation, ids[0]); 303 if (isNewRef()) { 304 hash.combine(ids[1], ids[2]); 305 } 306 return hash.valueOf(); 307 } 308 309 @Override clone()310 public Object clone() { 311 final OtpErlangRef newRef = (OtpErlangRef) super.clone(); 312 newRef.ids = ids.clone(); 313 return newRef; 314 } 315 } 316