blob: ee5c08f5dba6de19713cade8e14629ad05773dd5 [file] [log] [blame]
/* Copyright (c) 2002-2013 Sun Microsystems, Inc. All rights reserved
*
* This program is distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
package org.pantsbuild.jmake;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* This class implements writing a text stream representing a project database.
*
* @see TextProjectDatabaseReader for details.
*
* @author Benjy Weinberger
* 13 January 2013
*/
public class TextProjectDatabaseWriter {
private static Set<String> primitives = new LinkedHashSet<String>(
Arrays.asList("boolean", "byte", "char", "double", "float", "int", "long", "short",
"Z", "B", "C", "D", "F", "I", "J", "S"));
private ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Reusable temp buffer.
public void writeProjectDatabaseToFile(File outfile, Map<String, PCDEntry> pcd) {
try {
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outfile), "UTF-8"));
try {
writeProjectDatabase(out, pcd);
} finally {
out.close();
}
} catch (FileNotFoundException e) {
throw new PrivateException(e);
} catch (UnsupportedEncodingException e) {
throw new PrivateException(e);
} catch (IOException e) {
throw new PrivateException(e);
}
}
public void writeProjectDatabase(Writer out, Map<String,PCDEntry> pcd) {
try {
out.write("pcd entries:\n");
out.write(Integer.toString(pcd.size()));
out.write(" items\n");
Map<String, Set<String>> depsBySource = new LinkedHashMap<String, Set<String>>();
for (PCDEntry entry : pcd.values()) {
writePCDEntry(out, entry);
Set<String> deps = depsBySource.get(entry.javaFileFullPath);
if (deps == null) {
deps = new LinkedHashSet<String>();
depsBySource.put(entry.javaFileFullPath, deps);
}
addDepsFromClassInfo(deps, entry.oldClassInfo);
}
// Write out dependency information. Note that we don't need to read this back to recreate
// the PCD. We write it out here just as a convenience, so that external readers of the PDB
// file don't have to grok our internal ClassInfo structures.
out.write("dependencies:\n");
out.write(Integer.toString(depsBySource.size()));
out.write(" items\n");
for (Map.Entry<String, Set<String>> item : depsBySource.entrySet()) {
out.write(item.getKey());
for (String s : item.getValue()) {
out.write('\t');
out.write(s);
}
out.write('\n');
}
} catch (IOException e) {
throw new PrivateException(e);
}
}
private void addDepsFromClassInfo(Set<String> deps, ClassInfo ci) {
for (String s : ci.cpoolRefsToClasses) {
int i = 0;
int j = s.length();
// Fix some inconsistencies in how we represent types internally:
// Despite the comment on ci.cpoolRefsToClasses, class names may be
// representing in it with '['s and with '@', '#' instead of 'L', ';'.
while (s.charAt(i) == '[') i++;
if (s.charAt(i) == '@') i++;
if (s.endsWith("#")) j--;
int k = s.indexOf('$');
// Take the outer class, on references to nested classes.
if (k != -1) j = k;
if (i > 0 || j < s.length())
s = s.substring(i, j);
// We don't need to record deps on primitive types, or arrays of them.
if (!primitives.contains(s))
deps.add(s);
}
}
private void writePCDEntry(Writer out, PCDEntry entry) {
try {
out.write(entry.className);
out.write('\t');
out.write(entry.javaFileFullPath);
out.write('\t');
out.write(Long.toString(entry.oldClassFileLastModified));
out.write('\t');
out.write(Long.toString(entry.oldClassFileFingerprint));
out.write('\t');
out.write(classInfoToBase64(entry.oldClassInfo));
out.write('\n');
} catch (IOException e) {
throw new PrivateException(e);
}
}
private char[] classInfoToBase64(ClassInfo ci) {
baos.reset();
try {
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ci);
oos.close();
} catch (IOException e) {
throw new PrivateException(e);
}
return Base64.encode(baos.toByteArray());
}
}