20
20
21
21
import java .io .Serializable ;
22
22
import java .util .Collection ;
23
- import java .util .Collections ;
24
23
import java .util .LinkedHashMap ;
25
24
import java .util .Map ;
25
+ import java .util .Objects ;
26
26
27
27
/**
28
28
* Represents the location of an element within a model source file.
@@ -40,11 +40,15 @@ public class InputLocation implements Serializable, InputLocationTracker {
40
40
private final Map <Object , InputLocation > locations ;
41
41
private final InputLocation importedFrom ;
42
42
43
+ private volatile int hashCode = 0 ; // Cached hashCode for performance
44
+
45
+ private static final InputLocation EMPTY = new InputLocation (-1 , -1 );
46
+
43
47
public InputLocation (InputSource source ) {
44
48
this .lineNumber = -1 ;
45
49
this .columnNumber = -1 ;
46
50
this .source = source ;
47
- this .locations = Collections .singletonMap (0 , this );
51
+ this .locations = ImmutableCollections .singletonMap (0 , this );
48
52
this .importedFrom = null ;
49
53
}
50
54
@@ -60,8 +64,9 @@ public InputLocation(int lineNumber, int columnNumber, InputSource source, Objec
60
64
this .lineNumber = lineNumber ;
61
65
this .columnNumber = columnNumber ;
62
66
this .source = source ;
63
- this .locations =
64
- selfLocationKey != null ? Collections .singletonMap (selfLocationKey , this ) : Collections .emptyMap ();
67
+ this .locations = selfLocationKey != null
68
+ ? ImmutableCollections .singletonMap (selfLocationKey , this )
69
+ : ImmutableCollections .emptyMap ();
65
70
this .importedFrom = null ;
66
71
}
67
72
@@ -73,12 +78,75 @@ public InputLocation(int lineNumber, int columnNumber, InputSource source, Map<O
73
78
this .importedFrom = null ;
74
79
}
75
80
76
- public InputLocation (InputLocation original ) {
77
- this .lineNumber = original .lineNumber ;
78
- this .columnNumber = original .columnNumber ;
79
- this .source = original .source ;
80
- this .locations = original .locations ;
81
- this .importedFrom = original .importedFrom ;
81
+ // Factory methods
82
+
83
+ public static InputLocation of () {
84
+ return EMPTY ;
85
+ }
86
+
87
+ /**
88
+ * Creates a new InputLocation with the specified source.
89
+ * The created instance is processed through ModelObjectProcessor for optimization.
90
+ *
91
+ * @param source the input source
92
+ * @return a new InputLocation instance
93
+ */
94
+ public static InputLocation of (InputSource source ) {
95
+ return ModelObjectProcessor .processObject (new InputLocation (source ));
96
+ }
97
+
98
+ /**
99
+ * Creates a new InputLocation with the specified line and column numbers.
100
+ * The created instance is processed through ModelObjectProcessor for optimization.
101
+ *
102
+ * @param lineNumber the line number
103
+ * @param columnNumber the column number
104
+ * @return a new InputLocation instance
105
+ */
106
+ public static InputLocation of (int lineNumber , int columnNumber ) {
107
+ return ModelObjectProcessor .processObject (new InputLocation (lineNumber , columnNumber ));
108
+ }
109
+
110
+ /**
111
+ * Creates a new InputLocation with the specified line number, column number, and source.
112
+ * The created instance is processed through ModelObjectProcessor for optimization.
113
+ *
114
+ * @param lineNumber the line number
115
+ * @param columnNumber the column number
116
+ * @param source the input source
117
+ * @return a new InputLocation instance
118
+ */
119
+ public static InputLocation of (int lineNumber , int columnNumber , InputSource source ) {
120
+ return ModelObjectProcessor .processObject (new InputLocation (lineNumber , columnNumber , source ));
121
+ }
122
+
123
+ /**
124
+ * Creates a new InputLocation with the specified line number, column number, source, and self location key.
125
+ * The created instance is processed through ModelObjectProcessor for optimization.
126
+ *
127
+ * @param lineNumber the line number
128
+ * @param columnNumber the column number
129
+ * @param source the input source
130
+ * @param selfLocationKey the self location key
131
+ * @return a new InputLocation instance
132
+ */
133
+ public static InputLocation of (int lineNumber , int columnNumber , InputSource source , Object selfLocationKey ) {
134
+ return ModelObjectProcessor .processObject (new InputLocation (lineNumber , columnNumber , source , selfLocationKey ));
135
+ }
136
+
137
+ /**
138
+ * Creates a new InputLocation with the specified line number, column number, source, and locations map.
139
+ * The created instance is processed through ModelObjectProcessor for optimization.
140
+ *
141
+ * @param lineNumber the line number
142
+ * @param columnNumber the column number
143
+ * @param source the input source
144
+ * @param locations the locations map
145
+ * @return a new InputLocation instance
146
+ */
147
+ public static InputLocation of (
148
+ int lineNumber , int columnNumber , InputSource source , Map <Object , InputLocation > locations ) {
149
+ return ModelObjectProcessor .processObject (new InputLocation (lineNumber , columnNumber , source , locations ));
82
150
}
83
151
84
152
public int getLineNumber () {
@@ -184,21 +252,83 @@ public static InputLocation merge(InputLocation target, InputLocation source, Co
184
252
return new InputLocation (-1 , -1 , InputSource .merge (source .getSource (), target .getSource ()), locations );
185
253
} // -- InputLocation merge( InputLocation, InputLocation, java.util.Collection )
186
254
255
+ @ Override
256
+ public boolean equals (Object o ) {
257
+ if (this == o ) {
258
+ return true ;
259
+ }
260
+ if (o == null || getClass () != o .getClass ()) {
261
+ return false ;
262
+ }
263
+ InputLocation that = (InputLocation ) o ;
264
+ return lineNumber == that .lineNumber
265
+ && columnNumber == that .columnNumber
266
+ && Objects .equals (source , that .source )
267
+ && safeLocationsEquals (this , locations , that , that .locations )
268
+ && Objects .equals (importedFrom , that .importedFrom );
269
+ }
270
+
187
271
/**
188
- * Class StringFormatter.
189
- *
190
- * @version $Revision$ $Date$
272
+ * Safely compares two locations maps, treating self-references as equal.
191
273
*/
192
- public interface StringFormatter {
274
+ private static boolean safeLocationsEquals (
275
+ InputLocation this1 ,
276
+ Map <Object , InputLocation > map1 ,
277
+ InputLocation this2 ,
278
+ Map <Object , InputLocation > map2 ) {
279
+ if (map1 == map2 ) {
280
+ return true ;
281
+ }
282
+ if (map1 == null || map2 == null ) {
283
+ return false ;
284
+ }
285
+ if (map1 .size () != map2 .size ()) {
286
+ return false ;
287
+ }
193
288
194
- // -----------/
195
- // - Methods -/
196
- // -----------/
289
+ for (Map .Entry <Object , InputLocation > entry1 : map1 .entrySet ()) {
290
+ Object key = entry1 .getKey ();
291
+ InputLocation value1 = entry1 .getValue ();
292
+ InputLocation value2 = map2 .get (key );
197
293
198
- /**
199
- * Method toString.
200
- */
201
- String toString (InputLocation location );
294
+ if (value1 == this1 ) {
295
+ if (value2 == this2 ) {
296
+ continue ;
297
+ }
298
+ return false ;
299
+ } else {
300
+ if (Objects .equals (value1 , value2 )) {
301
+ continue ;
302
+ }
303
+ return false ;
304
+ }
305
+ }
306
+
307
+ return true ;
308
+ }
309
+
310
+ @ Override
311
+ public int hashCode () {
312
+ int result = hashCode ;
313
+ if (result == 0 ) {
314
+ result = Objects .hash (lineNumber , columnNumber , source , safeHash (locations ), importedFrom );
315
+ hashCode = result ;
316
+ }
317
+ return result ;
318
+ }
319
+
320
+ public int safeHash (Map <Object , InputLocation > locations ) {
321
+ if (locations == null ) {
322
+ return 0 ;
323
+ }
324
+ int result = 1 ;
325
+ for (Map .Entry <Object , InputLocation > entry : locations .entrySet ()) {
326
+ result = 31 * result + Objects .hashCode (entry .getKey ());
327
+ if (entry .getValue () != this ) {
328
+ result = 31 * result + Objects .hashCode (entry .getValue ());
329
+ }
330
+ }
331
+ return result ;
202
332
}
203
333
204
334
@ Override
0 commit comments