xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Definitions.java (revision 4323bb1188e6043af8c6bb4e3c91b394acba623c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
22  * Portions Copyright (c) 2018, Chris Fraire <cfraire@me.com>.
23  */
24 package org.opengrok.indexer.analysis;
25 
26 import org.opengrok.indexer.util.WhitelistObjectInputFilter;
27 
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.IOException;
31 import java.io.ObjectInputFilter;
32 import java.io.ObjectInputStream;
33 import java.io.ObjectOutputStream;
34 import java.io.Serializable;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Set;
41 
42 public class Definitions implements Serializable {
43 
44     private static final long serialVersionUID = 1191703801007779489L;
45 
46     private static final ObjectInputFilter serialFilter = new WhitelistObjectInputFilter(
47             Definitions.class,
48             HashMap.class,
49             Map.Entry[].class,
50             Integer.class,
51             Number.class,
52             LineTagMap.class,
53             HashSet.class,
54             Tag.class,
55             ArrayList.class,
56             Object[].class
57     );
58 
59     // Per line sym -> tags mapping
60     public static class LineTagMap implements Serializable {
61 
62         private static final long serialVersionUID = 1191703801007779481L;
63         private final Map<String, Set<Tag>> sym_tags; //NOPMD
64 
LineTagMap()65         protected LineTagMap() {
66             this.sym_tags = new HashMap<>();
67         }
68     }
69     // line number -> tag map
70     private final Map<Integer, LineTagMap> line_maps;
71 
72     /**
73      * Map from symbol to the line numbers on which the symbol is defined.
74      */
75     private final Map<String, Set<Integer>> symbols;
76     /**
77      * List of all the tags.
78      */
79     private final List<Tag> tags;
80 
Definitions()81     public Definitions() {
82         symbols = new HashMap<>();
83         line_maps = new HashMap<>();
84         tags = new ArrayList<>();
85     }
86 
87     /**
88      * Reset all {@link Tag#used} values to {@code false}.
89      */
resetUnused()90     public void resetUnused() {
91         for (Tag tag : tags) {
92             tag.used = false;
93         }
94     }
95 
96     /**
97      * Get all symbols used in definitions.
98      *
99      * @return a set containing all the symbols
100      */
getSymbols()101     public Set<String> getSymbols() {
102         return symbols.keySet();
103     }
104 
105     /**
106      * Check if there is a tag for a symbol.
107      *
108      * @param symbol the symbol to check
109      * @return {@code true} if there is a tag for {@code symbol}
110      */
hasSymbol(String symbol)111     public boolean hasSymbol(String symbol) {
112         return symbols.containsKey(symbol);
113     }
114 
115     /**
116      * Check whether the specified symbol is defined on the given line.
117      *
118      * @param symbol the symbol to look for
119      * @param lineNumber the line to check
120      * @param strs type of definition(to be passed back to caller)
121      * @return {@code true} if {@code symbol} is defined on the specified line
122      */
hasDefinitionAt(String symbol, int lineNumber, String[] strs)123     public boolean hasDefinitionAt(String symbol, int lineNumber, String[] strs) {
124         Set<Integer> lines = symbols.get(symbol);
125         if (strs.length > 0) {
126             strs[0] = "none";
127         }
128 
129         // Get tag info
130         if (lines != null && lines.contains(lineNumber)) {
131             LineTagMap lineMap = line_maps.get(lineNumber);
132             if (lineMap != null) {
133                 for (Tag tag : lineMap.sym_tags.get(symbol)) {
134                     if (tag.used) {
135                         continue;
136                     }
137                     if (strs.length > 0) { //NOPMD
138                         strs[0] = tag.type;
139                     }
140                     tag.used = true;
141                     // Assume the first one
142                     return true;
143                 }
144             }
145         }
146         return false;
147     }
148 
149     /**
150      * Return the number of occurrences of definitions with the specified
151      * symbol.
152      *
153      * @param symbol the symbol to count the occurrences of
154      * @return the number of times the specified symbol is defined
155      */
occurrences(String symbol)156     public int occurrences(String symbol) {
157         Set<Integer> lines = symbols.get(symbol);
158         return lines == null ? 0 : lines.size();
159     }
160 
161     /**
162      * Return the number of distinct symbols.
163      *
164      * @return number of distinct symbols
165      */
numberOfSymbols()166     public int numberOfSymbols() {
167         return symbols.size();
168     }
169 
170     /**
171      * Get a list of all tags.
172      *
173      * @return all tags
174      */
getTags()175     public List<Tag> getTags() {
176         return tags;
177     }
178 
179     /**
180      * Get a list of all tags on given line.
181      *
182      * @param line line number
183      * @return list of tags
184      */
getTags(int line)185     public List<Tag> getTags(int line) {
186         LineTagMap lineMap = line_maps.get(line);
187         List<Tag> result = null;
188 
189         if (lineMap != null) {
190             result = new ArrayList<>();
191             for (Set<Tag> ltags : lineMap.sym_tags.values()) {
192                 result.addAll(ltags);
193             }
194         }
195 
196         return result;
197     }
198 
199     /**
200      * Class that represents a single tag.
201      */
202     public static class Tag implements Serializable {
203 
204         private static final long serialVersionUID = 1217869075425651465L;
205 
206         /**
207          * Line number of the tag.
208          */
209         public final int line;
210         /**
211          * The symbol used in the definition.
212          */
213         public final String symbol;
214         /**
215          * The type of the tag.
216          */
217         public final String type;
218         /**
219          * The full line on which the definition occurs.
220          */
221         public final String text;
222         /**
223          * Namespace/class of tag definition.
224          */
225         public final String namespace;
226         /**
227          * Scope of tag definition.
228          */
229         public final String signature;
230         /**
231          * The starting offset (possibly approximate) of {@link #symbol} from
232          * the start of the line.
233          */
234         public final int lineStart;
235         /**
236          * The ending offset (possibly approximate) of {@link #symbol} from
237          * the start of the line.
238          */
239         public final int lineEnd;
240 
241         /**
242          * A non-serialized marker for marking a tag to avoid its reuse.
243          */
244         private transient boolean used;
245 
Tag(int line, String symbol, String type, String text, String namespace, String signature, int lineStart, int lineEnd)246         protected Tag(int line, String symbol, String type, String text,
247                 String namespace, String signature, int lineStart,
248                 int lineEnd) {
249             this.line = line;
250             this.symbol = symbol;
251             this.type = type;
252             this.text = text;
253             this.namespace = namespace;
254             this.signature = signature;
255             this.lineStart = lineStart;
256             this.lineEnd = lineEnd;
257         }
258     }
259 
addTag(int line, String symbol, String type, String text, int lineStart, int lineEnd)260     public void addTag(int line, String symbol, String type, String text,
261             int lineStart, int lineEnd) {
262         addTag(line, symbol, type, text, null, null, lineStart, lineEnd);
263     }
264 
addTag(int line, String symbol, String type, String text, String namespace, String signature, int lineStart, int lineEnd)265     public void addTag(int line, String symbol, String type, String text,
266             String namespace, String signature, int lineStart, int lineEnd) {
267         Tag newTag = new Tag(line, symbol, type, text, namespace, signature,
268             lineStart, lineEnd);
269         tags.add(newTag);
270         Set<Integer> lines = symbols.get(symbol);
271         if (lines == null) {
272             lines = new HashSet<>();
273             symbols.put(symbol, lines);
274         }
275         Integer aLine = line;
276         lines.add(aLine);
277 
278         // Get per line map
279         LineTagMap lineMap = line_maps.get(aLine);
280         if (lineMap == null) {
281             lineMap = new LineTagMap();
282             line_maps.put(aLine, lineMap);
283         }
284 
285         // Insert sym->tag map for this line
286         Set<Tag> ltags = lineMap.sym_tags.get(symbol);
287         if (ltags == null) {
288             ltags = new HashSet<>();
289             lineMap.sym_tags.put(symbol, ltags);
290         }
291         ltags.add(newTag);
292     }
293 
294     /**
295      * Create a binary representation of this object.
296      *
297      * @return a byte array representing this object
298      * @throws IOException if an error happens when writing to the array
299      */
serialize()300     public byte[] serialize() throws IOException {
301         try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); var oos = new ObjectOutputStream(bytes)) {
302             oos.writeObject(this);
303             return bytes.toByteArray();
304         }
305     }
306 
307     /**
308      * De-serialize a binary representation of a {@code Definitions} object.
309      *
310      * @param bytes a byte array containing the {@code Definitions} object
311      * @return a {@code Definitions} object
312      * @throws IOException if an I/O error happens when reading the array
313      * @throws ClassNotFoundException if the class definition for an object
314      * stored in the byte array cannot be found
315      * @throws ClassCastException if the array contains an object of another
316      * type than {@code Definitions}
317      */
deserialize(byte[] bytes)318     public static Definitions deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
319         try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
320             in.setObjectInputFilter(serialFilter);
321             return (Definitions) in.readObject();
322         }
323     }
324 }
325