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