/* * Bytecode analysis framework * Copyright (C) 2005, University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.umd.cs.findbugs.ba.interproc; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.annotation.CheckForNull; import javax.annotation.WillClose; import edu.umd.cs.findbugs.ba.AnalysisContext; import edu.umd.cs.findbugs.classfile.FieldOrMethodDescriptor; import edu.umd.cs.findbugs.util.Util; /** * Property database for interprocedural analysis. * * @param <KeyType> key type: either MethodDescriptor or FieldDescriptor * @param <ValueType> value type: a value that summarizes some property of the associated key * @author David Hovemeyer */ public abstract class PropertyDatabase<KeyType extends FieldOrMethodDescriptor, ValueType> { private Map<KeyType, ValueType> propertyMap; /** * Constructor. * Creates an empty property database. */ protected PropertyDatabase() { this.propertyMap = new HashMap<KeyType, ValueType>(); } /** * Set a property. * * @param key the key * @param property the property */ public void setProperty(KeyType key, ValueType property) { propertyMap.put(key, property); } /** * Get a property. * * @param key the key * @return the property, or null if no property is set for this key */ public @CheckForNull ValueType getProperty(KeyType key) { return propertyMap.get(key); } public Set<KeyType> getKeys() { return propertyMap.keySet(); } public Collection<Map.Entry<KeyType, ValueType>> entrySet() { return propertyMap.entrySet(); } /** * Return whether or not the database is empty. * * @return true if the database is empty, false it it has at least one entry */ public boolean isEmpty() { return propertyMap.isEmpty(); } /** * Remove a property. * * @param key the key * @return the old property, or null if there was no property defined for this key */ public ValueType removeProperty(KeyType key) { return propertyMap.remove(key); } /** * Read property database from given file. * * @param fileName name of the database file * @throws IOException * @throws MethodPropertyDatabaseFormatException */ public void readFromFile(String fileName) throws IOException, PropertyDatabaseFormatException { read(new FileInputStream(fileName)); } /** * Read property database from an input stream. * The InputStream is guaranteed to be closed, even if an * exception is thrown. * * @param in the InputStream * @throws IOException * @throws MethodPropertyDatabaseFormatException */ public void read(@WillClose InputStream in) throws IOException, PropertyDatabaseFormatException { BufferedReader reader = null; try { reader = new BufferedReader( Util.getReader(in)); String line; while ((line = reader.readLine()) != null) { line = line.trim(); if (line.equals("")) continue; int bar = line.indexOf('|'); if (bar < 0) { throw new PropertyDatabaseFormatException( "Invalid property database: missing separator"); } KeyType key = parseKey(line.substring(0, bar)); ValueType property = decodeProperty(line.substring(bar+1)); setProperty(key, property); } } finally { try { if (reader != null) reader.close(); } catch (IOException e) { // Ignore } } } /** * Write property database to given file. * * @param fileName name of the database file * @throws IOException */ public void writeToFile(String fileName) throws IOException { write(new FileOutputStream(fileName)); } // @SuppressWarnings("unchecked") // private KeyType intern(XFactory xFactory, KeyType key) { // KeyType result = key; // try { // if (key instanceof XField) // return (KeyType) xFactory.intern((XField)key); // else if (key instanceof XMethod) // return (KeyType) xFactory.intern((XMethod)key); // } catch (Exception e) { // return key; // } // return result; // } /** * Write property database to an OutputStream. * The OutputStream is guaranteed to be closed, even if an * exception is thrown. * * @param out the OutputStream * @throws IOException */ public void write(@WillClose OutputStream out) throws IOException { BufferedWriter writer = null; boolean missingClassWarningsSuppressed = AnalysisContext.currentAnalysisContext().setMissingClassWarningsSuppressed(true); try { writer = new BufferedWriter( new OutputStreamWriter(out, Charset.forName("UTF-8"))); TreeSet<KeyType> sortedMethodSet = new TreeSet<KeyType>(); sortedMethodSet.addAll(propertyMap.keySet()); for (KeyType key : sortedMethodSet) { if (AnalysisContext.currentAnalysisContext().isApplicationClass(key.getClassDescriptor())) { ValueType property = propertyMap.get(key); writeKey(writer, key); writer.write("|"); writer.write(encodeProperty(property)); writer.write("\n"); } } } finally { AnalysisContext.currentAnalysisContext().setMissingClassWarningsSuppressed(missingClassWarningsSuppressed); try { if (writer != null) writer.close(); } catch (IOException e) { // Ignore } } } /** * Parse a key from a String. * * @param s a String * @return the decoded key * @throws PropertyDatabaseFormatException */ protected abstract KeyType parseKey(String s) throws PropertyDatabaseFormatException; /** * Write an encoded key to given Writer. * * @param writer the Writer * @param key the key */ protected abstract void writeKey(Writer writer, KeyType key) throws IOException; /** * Subclasses must define this to instantiate the actual property * value from its string encoding. * * @param propStr String containing the encoded property * @return the property * @throws MethodPropertyDatabaseFormatException */ protected abstract ValueType decodeProperty(String propStr) throws PropertyDatabaseFormatException; /** * Subclasses must define this to encode a property * as a string for output to a file. * * @param property the property * @return a String which encodes the property */ protected abstract String encodeProperty(ValueType property); }