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.api.model;
20  
21  import java.io.Serializable;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.LinkedHashMap;
25  import java.util.Map;
26  
27  /**
28   * Class InputLocation.
29   */
30  public class InputLocation implements Serializable, InputLocationTracker {
31      private final int lineNumber;
32      private final int columnNumber;
33      private final InputSource source;
34      private final Map<Object, InputLocation> locations;
35      private final InputLocation importedFrom;
36  
37      public InputLocation(InputSource source) {
38          this.lineNumber = -1;
39          this.columnNumber = -1;
40          this.source = source;
41          this.locations = Collections.singletonMap(0, this);
42          this.importedFrom = null;
43      }
44  
45      public InputLocation(int lineNumber, int columnNumber) {
46          this(lineNumber, columnNumber, null, null);
47      }
48  
49      public InputLocation(int lineNumber, int columnNumber, InputSource source) {
50          this(lineNumber, columnNumber, source, null);
51      }
52  
53      public InputLocation(int lineNumber, int columnNumber, InputSource source, Object selfLocationKey) {
54          this.lineNumber = lineNumber;
55          this.columnNumber = columnNumber;
56          this.source = source;
57          this.locations =
58                  selfLocationKey != null ? Collections.singletonMap(selfLocationKey, this) : Collections.emptyMap();
59          this.importedFrom = null;
60      }
61  
62      public InputLocation(int lineNumber, int columnNumber, InputSource source, Map<Object, InputLocation> locations) {
63          this.lineNumber = lineNumber;
64          this.columnNumber = columnNumber;
65          this.source = source;
66          this.locations = ImmutableCollections.copy(locations);
67          this.importedFrom = null;
68      }
69  
70      public InputLocation(InputLocation original) {
71          this.lineNumber = original.lineNumber;
72          this.columnNumber = original.columnNumber;
73          this.source = original.source;
74          this.locations = original.locations;
75          this.importedFrom = original.importedFrom;
76      }
77  
78      public int getLineNumber() {
79          return lineNumber;
80      }
81  
82      public int getColumnNumber() {
83          return columnNumber;
84      }
85  
86      public InputSource getSource() {
87          return source;
88      }
89  
90      public InputLocation getLocation(Object key) {
91          return locations != null ? locations.get(key) : null;
92      }
93  
94      public Map<Object, InputLocation> getLocations() {
95          return locations;
96      }
97  
98      /**
99       * Gets the parent InputLocation where this InputLocation may have been imported from.
100      * Can return {@code null}.
101      *
102      * @return InputLocation
103      * @since 4.0.0
104      */
105     public InputLocation getImportedFrom() {
106         return importedFrom;
107     }
108 
109     /**
110      * Merges the {@code source} location into the {@code target} location.
111      *
112      * @param target the target location
113      * @param source the source location
114      * @param sourceDominant the boolean indicating of {@code source} is dominant compared to {@code target}
115      * @return the merged location
116      */
117     public static InputLocation merge(InputLocation target, InputLocation source, boolean sourceDominant) {
118         if (source == null) {
119             return target;
120         } else if (target == null) {
121             return source;
122         }
123 
124         Map<Object, InputLocation> locations;
125         Map<Object, InputLocation> sourceLocations = source.locations;
126         Map<Object, InputLocation> targetLocations = target.locations;
127         if (sourceLocations == null) {
128             locations = targetLocations;
129         } else if (targetLocations == null) {
130             locations = sourceLocations;
131         } else {
132             locations = new LinkedHashMap<>();
133             locations.putAll(sourceDominant ? targetLocations : sourceLocations);
134             locations.putAll(sourceDominant ? sourceLocations : targetLocations);
135         }
136 
137         return new InputLocation(-1, -1, InputSource.merge(source.getSource(), target.getSource()), locations);
138     } // -- InputLocation merge( InputLocation, InputLocation, boolean )
139 
140     /**
141      * Merges the {@code source} location into the {@code target} location.
142      * This method is used when the locations refer to lists and also merges the indices.
143      *
144      * @param target the target location
145      * @param source the source location
146      * @param indices the list of integers for the indices
147      * @return the merged location
148      */
149     public static InputLocation merge(InputLocation target, InputLocation source, Collection<Integer> indices) {
150         if (source == null) {
151             return target;
152         } else if (target == null) {
153             return source;
154         }
155 
156         Map<Object, InputLocation> locations;
157         Map<Object, InputLocation> sourceLocations = source.locations;
158         Map<Object, InputLocation> targetLocations = target.locations;
159         if (sourceLocations == null) {
160             locations = targetLocations;
161         } else if (targetLocations == null) {
162             locations = sourceLocations;
163         } else {
164             locations = new LinkedHashMap<>();
165             for (int index : indices) {
166                 InputLocation location;
167                 if (index < 0) {
168                     location = sourceLocations.get(~index);
169                 } else {
170                     location = targetLocations.get(index);
171                 }
172                 locations.put(locations.size(), location);
173             }
174         }
175 
176         return new InputLocation(-1, -1, InputSource.merge(source.getSource(), target.getSource()), locations);
177     } // -- InputLocation merge( InputLocation, InputLocation, java.util.Collection )
178 
179     /**
180      * Class StringFormatter.
181      *
182      * @version $Revision$ $Date$
183      */
184     public interface StringFormatter {
185 
186         // -----------/
187         // - Methods -/
188         // -----------/
189 
190         /**
191          * Method toString.
192          */
193         String toString(InputLocation location);
194     }
195 
196     @Override
197     public String toString() {
198         return String.format("%s @ %d:%d", source.getLocation(), lineNumber, columnNumber);
199     }
200 }