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&lt;node.id&gt;
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