001: /*
002: * Bytecode analysis framework
003: * Copyright (C) 2005, University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba.interproc;
021:
022: import java.io.BufferedReader;
023: import java.io.BufferedWriter;
024: import java.io.FileInputStream;
025: import java.io.FileOutputStream;
026: import java.io.IOException;
027: import java.io.InputStream;
028: import java.io.OutputStream;
029: import java.io.OutputStreamWriter;
030: import java.io.Writer;
031: import java.nio.charset.Charset;
032: import java.util.HashMap;
033: import java.util.Map;
034: import java.util.Set;
035: import java.util.TreeSet;
036:
037: import edu.umd.cs.findbugs.ba.AnalysisContext;
038: import edu.umd.cs.findbugs.classfile.FieldOrMethodDescriptor;
039: import edu.umd.cs.findbugs.util.Util;
040:
041: /**
042: * Property database for interprocedural analysis.
043: *
044: * @param <KeyType> key type: either MethodDescriptor or FieldDescriptor
045: * @param <ValueType> value type: a value that summarizes some property of the associated key
046: * @author David Hovemeyer
047: */
048: public abstract class PropertyDatabase<KeyType extends FieldOrMethodDescriptor, ValueType> {
049: private Map<KeyType, ValueType> propertyMap;
050:
051: /**
052: * Constructor.
053: * Creates an empty property database.
054: */
055: protected PropertyDatabase() {
056: this .propertyMap = new HashMap<KeyType, ValueType>();
057: }
058:
059: /**
060: * Set a property.
061: *
062: * @param key the key
063: * @param property the property
064: */
065: public void setProperty(KeyType key, ValueType property) {
066: propertyMap.put(key, property);
067: }
068:
069: /**
070: * Get a property.
071: *
072: * @param key the key
073: * @return the property, or null if no property is set for this key
074: */
075: public ValueType getProperty(KeyType key) {
076: return propertyMap.get(key);
077: }
078:
079: public Set<KeyType> getKeys() {
080: return propertyMap.keySet();
081: }
082:
083: /**
084: * Return whether or not the database is empty.
085: *
086: * @return true if the database is empty, false it it has at least one entry
087: */
088: public boolean isEmpty() {
089: return propertyMap.isEmpty();
090: }
091:
092: /**
093: * Remove a property.
094: *
095: * @param key the key
096: * @return the old property, or null if there was no property defined for this key
097: */
098: public ValueType removeProperty(KeyType key) {
099: return propertyMap.remove(key);
100: }
101:
102: /**
103: * Read property database from given file.
104: *
105: * @param fileName name of the database file
106: * @throws IOException
107: * @throws MethodPropertyDatabaseFormatException
108: */
109: public void readFromFile(String fileName) throws IOException,
110: PropertyDatabaseFormatException {
111: read(new FileInputStream(fileName));
112: }
113:
114: /**
115: * Read property database from an input stream.
116: * The InputStream is guaranteed to be closed, even if an
117: * exception is thrown.
118: *
119: * @param in the InputStream
120: * @throws IOException
121: * @throws MethodPropertyDatabaseFormatException
122: */
123: public void read(InputStream in) throws IOException,
124: PropertyDatabaseFormatException {
125: BufferedReader reader = null;
126:
127: try {
128: reader = new BufferedReader(Util.getReader(in));
129: String line;
130: while ((line = reader.readLine()) != null) {
131: line = line.trim();
132: if (line.equals(""))
133: continue;
134: int bar = line.indexOf('|');
135: if (bar < 0) {
136: throw new PropertyDatabaseFormatException(
137: "Invalid property database: missing separator");
138: }
139: KeyType key = parseKey(line.substring(0, bar));
140: ValueType property = decodeProperty(line
141: .substring(bar + 1));
142:
143: setProperty(key, property);
144: }
145: } finally {
146: try {
147: if (reader != null)
148: reader.close();
149: } catch (IOException e) {
150: // Ignore
151: }
152: }
153: }
154:
155: /**
156: * Write property database to given file.
157: *
158: * @param fileName name of the database file
159: * @throws IOException
160: */
161: public void writeToFile(String fileName) throws IOException {
162: write(new FileOutputStream(fileName));
163: }
164:
165: // @SuppressWarnings("unchecked")
166: // private KeyType intern(XFactory xFactory, KeyType key) {
167: // KeyType result = key;
168: // try {
169: // if (key instanceof XField)
170: // return (KeyType) xFactory.intern((XField)key);
171: // else if (key instanceof XMethod)
172: // return (KeyType) xFactory.intern((XMethod)key);
173: // } catch (Exception e) {
174: // return key;
175: // }
176: // return result;
177: // }
178:
179: /**
180: * Write property database to an OutputStream.
181: * The OutputStream is guaranteed to be closed, even if an
182: * exception is thrown.
183: *
184: * @param out the OutputStream
185: * @throws IOException
186: */
187: public void write(OutputStream out) throws IOException {
188: BufferedWriter writer = null;
189: boolean missingClassWarningsSuppressed = AnalysisContext
190: .currentAnalysisContext()
191: .setMissingClassWarningsSuppressed(true);
192:
193: try {
194: writer = new BufferedWriter(new OutputStreamWriter(out,
195: Charset.forName("UTF-8")));
196:
197: TreeSet<KeyType> sortedMethodSet = new TreeSet<KeyType>();
198: sortedMethodSet.addAll(propertyMap.keySet());
199: for (KeyType key : sortedMethodSet) {
200: if (AnalysisContext.currentAnalysisContext()
201: .isApplicationClass(
202: key.getClassDescriptor()
203: .toDottedClassName())) {
204:
205: ValueType property = propertyMap.get(key);
206: writeKey(writer, key/*intern(xFactory, key)*/);
207: writer.write("|");
208: writer.write(encodeProperty(property));
209: writer.write("\n");
210: }
211: }
212: } finally {
213: AnalysisContext.currentAnalysisContext()
214: .setMissingClassWarningsSuppressed(
215: missingClassWarningsSuppressed);
216:
217: try {
218: if (writer != null)
219: writer.close();
220: } catch (IOException e) {
221: // Ignore
222: }
223: }
224: }
225:
226: /**
227: * Parse a key from a String.
228: *
229: * @param s a String
230: * @return the decoded key
231: * @throws PropertyDatabaseFormatException
232: */
233: protected abstract KeyType parseKey(String s)
234: throws PropertyDatabaseFormatException;
235:
236: /**
237: * Write an encoded key to given Writer.
238: *
239: * @param writer the Writer
240: * @param key the key
241: */
242: protected abstract void writeKey(Writer writer, KeyType key)
243: throws IOException;
244:
245: /**
246: * Subclasses must define this to instantiate the actual property
247: * value from its string encoding.
248: *
249: * @param propStr String containing the encoded property
250: * @return the property
251: * @throws MethodPropertyDatabaseFormatException
252: */
253: protected abstract ValueType decodeProperty(String propStr)
254: throws PropertyDatabaseFormatException;
255:
256: /**
257: * Subclasses must define this to encode a property
258: * as a string for output to a file.
259: *
260: * @param property the property
261: * @return a String which encodes the property
262: */
263: protected abstract String encodeProperty(ValueType property);
264:
265: }
|