View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.model;
20  
21  import java.util.stream.Collectors;
22  
23  /**
24   * Class InputLocation.
25   *
26   * @version $Revision$ $Date$
27   */
28  @SuppressWarnings("all")
29  public final class InputLocation implements java.io.Serializable, Cloneable, InputLocationTracker {
30  
31      // --------------------------/
32      // - Class/Member Variables -/
33      // --------------------------/
34  
35      /**
36       * The one-based line number. The value will be non-positive if
37       * unknown.
38       */
39      private int lineNumber = -1;
40  
41      /**
42       * The one-based column number. The value will be non-positive
43       * if unknown.
44       */
45      private int columnNumber = -1;
46  
47      /**
48       * Field source.
49       */
50      private InputSource source;
51  
52      /**
53       * Field locations.
54       */
55      private java.util.Map<Object, InputLocation> locations;
56  
57      /**
58       * Field location.
59       */
60      private InputLocation location;
61  
62      /**
63       * Field importedFrom.
64       */
65      private InputLocation importedFrom;
66  
67      /**
68       * Cached hashCode for performance.
69       */
70      private volatile int hashCode = 0;
71  
72      // ----------------/
73      // - Constructors -/
74      // ----------------/
75  
76      /**
77       * Creates a new InputLocation from an API model InputLocation.
78       * This constructor is used for converting between the API model and the compat model.
79       *
80       * @param location the API model InputLocation to convert from
81       */
82      public InputLocation(org.apache.maven.api.model.InputLocation location) {
83          this.lineNumber = location.getLineNumber();
84          this.columnNumber = location.getColumnNumber();
85          this.source = location.getSource() != null ? new InputSource(location.getSource()) : null;
86          this.locations = location.getLocations().isEmpty()
87                  ? null
88                  : location.getLocations().entrySet().stream()
89                          .collect(Collectors.toMap(
90                                  e -> e.getKey(),
91                                  e -> e.getValue() == location ? this : new InputLocation(e.getValue())));
92          this.importedFrom = location.getImportedFrom() != null ? new InputLocation(location.getImportedFrom()) : null;
93      }
94  
95      public InputLocation(int lineNumber, int columnNumber) {
96          this.lineNumber = lineNumber;
97          this.columnNumber = columnNumber;
98      } // -- org.apache.maven.model.InputLocation(int, int)
99  
100     public InputLocation(int lineNumber, int columnNumber, InputSource source) {
101         this.lineNumber = lineNumber;
102         this.columnNumber = columnNumber;
103         this.source = source;
104     } // -- org.apache.maven.model.InputLocation(int, int, InputSource)
105 
106     // -----------/
107     // - Methods -/
108     // -----------/
109 
110     /**
111      * Method clone.
112      *
113      * @return InputLocation
114      */
115     public InputLocation clone() {
116         try {
117             InputLocation copy = (InputLocation) super.clone();
118 
119             if (copy.locations != null) {
120                 copy.locations = new java.util.LinkedHashMap(copy.locations);
121             }
122 
123             return copy;
124         } catch (Exception ex) {
125             throw (RuntimeException)
126                     new UnsupportedOperationException(getClass().getName() + " does not support clone()").initCause(ex);
127         }
128     } // -- InputLocation clone()
129 
130     /**
131      * Get the one-based column number. The value will be
132      * non-positive if unknown.
133      *
134      * @return int
135      */
136     public int getColumnNumber() {
137         return this.columnNumber;
138     } // -- int getColumnNumber()
139 
140     /**
141      * Get the one-based line number. The value will be
142      * non-positive if unknown.
143      *
144      * @return int
145      */
146     public int getLineNumber() {
147         return this.lineNumber;
148     } // -- int getLineNumber()
149 
150     /**
151      * Gets the InputLocation for a specific nested element key.
152      *
153      * @param key the key to look up
154      * @return the InputLocation for the specified key, or null if not found
155      */
156     @Override
157     public InputLocation getLocation(Object key) {
158         if (key instanceof String string) {
159             switch (string) {
160                 case "": {
161                     return this.location;
162                 }
163                 default: {
164                     return getOtherLocation(key);
165                 }
166             }
167         } else {
168             return getOtherLocation(key);
169         }
170     } // -- InputLocation getLocation( Object )
171 
172     /**
173      * Gets the map of nested element locations within this location.
174      *
175      * @return a map of keys to InputLocation instances for nested elements, or null if none
176      */
177     public java.util.Map<Object, InputLocation> getLocations() {
178         return locations;
179     } // -- java.util.Map<Object, InputLocation> getLocations()
180 
181     /**
182      * Sets the InputLocation for a specific nested element key.
183      *
184      * @param key the key to set the location for
185      * @param location the InputLocation to associate with the key
186      */
187     @Override
188     public void setLocation(Object key, InputLocation location) {
189         if (key instanceof String string) {
190             switch (string) {
191                 case "": {
192                     this.location = location;
193                     return;
194                 }
195                 default: {
196                     setOtherLocation(key, location);
197                     return;
198                 }
199             }
200         } else {
201             setOtherLocation(key, location);
202         }
203     } // -- void setLocation( Object, InputLocation )
204 
205     /**
206      * Sets the InputLocation for a specific nested element key in the locations map.
207      * This is a helper method that manages the internal locations map.
208      *
209      * @param key the key to set the location for
210      * @param location the InputLocation to associate with the key
211      */
212     public void setOtherLocation(Object key, InputLocation location) {
213         if (location != null) {
214             if (this.locations == null) {
215                 this.locations = new java.util.LinkedHashMap<>();
216             }
217             this.locations.put(key, location);
218         }
219     } // -- void setOtherLocation( Object, InputLocation )
220 
221     /**
222      *
223      *
224      * @param key
225      * @return InputLocation
226      */
227     private InputLocation getOtherLocation(Object key) {
228         return (locations != null) ? locations.get(key) : null;
229     } // -- InputLocation getOtherLocation( Object )
230 
231     /**
232      * Get the source field.
233      *
234      * @return InputSource
235      */
236     public InputSource getSource() {
237         return this.source;
238     } // -- InputSource getSource()
239 
240     /**
241      * Gets the parent InputLocation where this InputLocation may have been imported from.
242      * Can return {@code null}.
243      *
244      * @return InputLocation
245      * @since 4.0.0
246      */
247     public InputLocation getImportedFrom() {
248         return importedFrom;
249     }
250 
251     /**
252      * Set the imported from location.
253      *
254      * @param importedFrom
255      */
256     public void setImportedFrom(InputLocation importedFrom) {
257         this.importedFrom = importedFrom;
258     }
259 
260     /**
261      * Method merge.
262      *
263      * @param target
264      * @param sourceDominant
265      * @param source
266      * @return InputLocation
267      */
268     public static InputLocation merge(InputLocation target, InputLocation source, boolean sourceDominant) {
269         if (source == null) {
270             return target;
271         } else if (target == null) {
272             return source;
273         }
274 
275         InputLocation result = new InputLocation(target.getLineNumber(), target.getColumnNumber(), target.getSource());
276 
277         java.util.Map<Object, InputLocation> locations;
278         java.util.Map<Object, InputLocation> sourceLocations = source.getLocations();
279         java.util.Map<Object, InputLocation> targetLocations = target.getLocations();
280         if (sourceLocations == null) {
281             locations = targetLocations;
282         } else if (targetLocations == null) {
283             locations = sourceLocations;
284         } else {
285             locations = new java.util.LinkedHashMap();
286             locations.putAll(sourceDominant ? targetLocations : sourceLocations);
287             locations.putAll(sourceDominant ? sourceLocations : targetLocations);
288         }
289         result.setLocations(locations);
290 
291         return result;
292     } // -- InputLocation merge( InputLocation, InputLocation, boolean )
293 
294     /**
295      * Method merge.
296      *
297      * @param target
298      * @param indices
299      * @param source
300      * @return InputLocation
301      */
302     public static InputLocation merge(
303             InputLocation target, InputLocation source, java.util.Collection<Integer> indices) {
304         if (source == null) {
305             return target;
306         } else if (target == null) {
307             return source;
308         }
309 
310         InputLocation result = new InputLocation(target.getLineNumber(), target.getColumnNumber(), target.getSource());
311 
312         java.util.Map<Object, InputLocation> locations;
313         java.util.Map<Object, InputLocation> sourceLocations = source.getLocations();
314         java.util.Map<Object, InputLocation> targetLocations = target.getLocations();
315         if (sourceLocations == null) {
316             locations = targetLocations;
317         } else if (targetLocations == null) {
318             locations = sourceLocations;
319         } else {
320             locations = new java.util.LinkedHashMap<>();
321             for (java.util.Iterator<Integer> it = indices.iterator(); it.hasNext(); ) {
322                 InputLocation location;
323                 Integer index = it.next();
324                 if (index.intValue() < 0) {
325                     location = sourceLocations.get(Integer.valueOf(~index.intValue()));
326                 } else {
327                     location = targetLocations.get(index);
328                 }
329                 locations.put(Integer.valueOf(locations.size()), location);
330             }
331         }
332         result.setLocations(locations);
333 
334         return result;
335     } // -- InputLocation merge( InputLocation, InputLocation, java.util.Collection )
336 
337     /**
338      *
339      *
340      * @param locations
341      */
342     public void setLocations(java.util.Map<Object, InputLocation> locations) {
343         this.locations = locations;
344     } // -- void setLocations( java.util.Map )
345 
346     /**
347      * Converts this compat model InputLocation to an API model InputLocation.
348      * This method is used for converting between the compat model and the API model.
349      *
350      * @return the equivalent API model InputLocation
351      */
352     public org.apache.maven.api.model.InputLocation toApiLocation() {
353         if (locations != null && locations.values().contains(this)) {
354             if (locations.size() == 1 && locations.values().iterator().next() == this) {
355                 return org.apache.maven.api.model.InputLocation.of(
356                         lineNumber,
357                         columnNumber,
358                         source != null ? source.toApiSource() : null,
359                         locations.keySet().iterator().next());
360             } else {
361                 return org.apache.maven.api.model.InputLocation.of(
362                         lineNumber, columnNumber, source != null ? source.toApiSource() : null);
363             }
364         } else {
365             return org.apache.maven.api.model.InputLocation.of(
366                     lineNumber,
367                     columnNumber,
368                     source != null ? source.toApiSource() : null,
369                     locations != null
370                             ? locations.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()
371                                     .toApiLocation()))
372                             : null);
373         }
374     }
375 
376     // -----------------/
377     // - Inner Classes -/
378     // -----------------/
379 
380     /**
381      * Class StringFormatter.
382      *
383      * @version $Revision$ $Date$
384      */
385     public abstract static class StringFormatter {
386 
387         // -----------/
388         // - Methods -/
389         // -----------/
390 
391         /**
392          * Method toString.
393          *
394          * @param location
395          * @return String
396          */
397         public abstract String toString(InputLocation location);
398     }
399 
400     @Override
401     public boolean equals(Object o) {
402         if (this == o) {
403             return true;
404         }
405         if (o == null || getClass() != o.getClass()) {
406             return false;
407         }
408         InputLocation that = (InputLocation) o;
409         return lineNumber == that.lineNumber
410                 && columnNumber == that.columnNumber
411                 && java.util.Objects.equals(source, that.source)
412                 && java.util.Objects.equals(locations, that.locations)
413                 && java.util.Objects.equals(importedFrom, that.importedFrom);
414     }
415 
416     @Override
417     public int hashCode() {
418         int result = hashCode;
419         if (result == 0) {
420             result = java.util.Objects.hash(lineNumber, columnNumber, source, locations, importedFrom);
421             hashCode = result;
422         }
423         return result;
424     }
425 
426     @Override
427     public String toString() {
428         return getLineNumber() + " : " + getColumnNumber() + ", " + getSource();
429     }
430 }