/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * See LICENSE.txt included in this distribution for the specific * language governing permissions and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at LICENSE.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. * Portions Copyright (c) 2018, Chris Fraire . */ package org.opengrok.indexer.analysis; import org.opengrok.indexer.util.WhitelistObjectInputFilter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; public class Definitions implements Serializable { private static final long serialVersionUID = 1191703801007779489L; private static final ObjectInputFilter serialFilter = new WhitelistObjectInputFilter( Definitions.class, HashMap.class, Map.Entry[].class, Integer.class, Number.class, LineTagMap.class, HashSet.class, Tag.class, ArrayList.class, Object[].class ); // Per line sym -> tags mapping public static class LineTagMap implements Serializable { private static final long serialVersionUID = 1191703801007779481L; private final Map> sym_tags; //NOPMD protected LineTagMap() { this.sym_tags = new HashMap<>(); } } // line number -> tag map private final Map line_maps; /** * Map from symbol to the line numbers on which the symbol is defined. */ private final Map> symbols; /** * List of all the tags. */ private final List tags; public Definitions() { symbols = new HashMap<>(); line_maps = new HashMap<>(); tags = new ArrayList<>(); } /** * Reset all {@link Tag#used} values to {@code false}. */ public void resetUnused() { for (Tag tag : tags) { tag.used = false; } } /** * Get all symbols used in definitions. * * @return a set containing all the symbols */ public Set getSymbols() { return symbols.keySet(); } /** * Check if there is a tag for a symbol. * * @param symbol the symbol to check * @return {@code true} if there is a tag for {@code symbol} */ public boolean hasSymbol(String symbol) { return symbols.containsKey(symbol); } /** * Check whether the specified symbol is defined on the given line. * * @param symbol the symbol to look for * @param lineNumber the line to check * @param strs type of definition(to be passed back to caller) * @return {@code true} if {@code symbol} is defined on the specified line */ public boolean hasDefinitionAt(String symbol, int lineNumber, String[] strs) { Set lines = symbols.get(symbol); if (strs.length > 0) { strs[0] = "none"; } // Get tag info if (lines != null && lines.contains(lineNumber)) { LineTagMap lineMap = line_maps.get(lineNumber); if (lineMap != null) { for (Tag tag : lineMap.sym_tags.get(symbol)) { if (tag.used) { continue; } if (strs.length > 0) { //NOPMD strs[0] = tag.type; } tag.used = true; // Assume the first one return true; } } } return false; } /** * Return the number of occurrences of definitions with the specified * symbol. * * @param symbol the symbol to count the occurrences of * @return the number of times the specified symbol is defined */ public int occurrences(String symbol) { Set lines = symbols.get(symbol); return lines == null ? 0 : lines.size(); } /** * Return the number of distinct symbols. * * @return number of distinct symbols */ public int numberOfSymbols() { return symbols.size(); } /** * Get a list of all tags. * * @return all tags */ public List getTags() { return tags; } /** * Get a list of all tags on given line. * * @param line line number * @return list of tags */ public List getTags(int line) { LineTagMap lineMap = line_maps.get(line); List result = null; if (lineMap != null) { result = new ArrayList<>(); for (Set ltags : lineMap.sym_tags.values()) { result.addAll(ltags); } } return result; } /** * Class that represents a single tag. */ public static class Tag implements Serializable { private static final long serialVersionUID = 1217869075425651465L; /** * Line number of the tag. */ public final int line; /** * The symbol used in the definition. */ public final String symbol; /** * The type of the tag. */ public final String type; /** * The full line on which the definition occurs. */ public final String text; /** * Namespace/class of tag definition. */ public final String namespace; /** * Scope of tag definition. */ public final String signature; /** * The starting offset (possibly approximate) of {@link #symbol} from * the start of the line. */ public final int lineStart; /** * The ending offset (possibly approximate) of {@link #symbol} from * the start of the line. */ public final int lineEnd; /** * A non-serialized marker for marking a tag to avoid its reuse. */ private transient boolean used; protected Tag(int line, String symbol, String type, String text, String namespace, String signature, int lineStart, int lineEnd) { this.line = line; this.symbol = symbol; this.type = type; this.text = text; this.namespace = namespace; this.signature = signature; this.lineStart = lineStart; this.lineEnd = lineEnd; } } public void addTag(int line, String symbol, String type, String text, int lineStart, int lineEnd) { addTag(line, symbol, type, text, null, null, lineStart, lineEnd); } public void addTag(int line, String symbol, String type, String text, String namespace, String signature, int lineStart, int lineEnd) { Tag newTag = new Tag(line, symbol, type, text, namespace, signature, lineStart, lineEnd); tags.add(newTag); Set lines = symbols.get(symbol); if (lines == null) { lines = new HashSet<>(); symbols.put(symbol, lines); } Integer aLine = line; lines.add(aLine); // Get per line map LineTagMap lineMap = line_maps.get(aLine); if (lineMap == null) { lineMap = new LineTagMap(); line_maps.put(aLine, lineMap); } // Insert sym->tag map for this line Set ltags = lineMap.sym_tags.get(symbol); if (ltags == null) { ltags = new HashSet<>(); lineMap.sym_tags.put(symbol, ltags); } ltags.add(newTag); } /** * Create a binary representation of this object. * * @return a byte array representing this object * @throws IOException if an error happens when writing to the array */ public byte[] serialize() throws IOException { try (ByteArrayOutputStream bytes = new ByteArrayOutputStream(); var oos = new ObjectOutputStream(bytes)) { oos.writeObject(this); return bytes.toByteArray(); } } /** * De-serialize a binary representation of a {@code Definitions} object. * * @param bytes a byte array containing the {@code Definitions} object * @return a {@code Definitions} object * @throws IOException if an I/O error happens when reading the array * @throws ClassNotFoundException if the class definition for an object * stored in the byte array cannot be found * @throws ClassCastException if the array contains an object of another * type than {@code Definitions} */ public static Definitions deserialize(byte[] bytes) throws IOException, ClassNotFoundException { try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes))) { in.setObjectInputFilter(serialFilter); return (Definitions) in.readObject(); } } }