# # add_file "contrib/monotree/gxl-1.0.dtd" # # patch "contrib/monotree/GXLViewer.java" # from [99d32abfebf2c8e88196a381fa4c295766b05ca0] # to [eb18a81fdf7578826b6d3d60a6dc11d249dffce4] # # patch "contrib/monotree/Log2Gxl.java" # from [3306f53fbdff2016e5274cc057ab15076100b1ed] # to [2052bf045a5db5665b9baa5e9eaa6e91895d55c7] # # patch "contrib/monotree/Makefile" # from [79fd899eb1b5b99f1b58a37571884807cfb59ac9] # to [693069811de8c93403779e13bd1a45429912e793] # # patch "contrib/monotree/Monotone.java" # from [eabe200248a5bb7f707c224a7150d3ad321464e5] # to [9a979d5d0fcd116294f3a2c38e18962849403d71] # # patch "contrib/monotree/gxl-1.0.dtd" # from [] # to [f37a687598c4a5cdc32edc7aff66825ed2b85f38] # --- contrib/monotree/GXLViewer.java +++ contrib/monotree/GXLViewer.java @@ -401,7 +401,7 @@ public void run() { try { logger.fine("Getting log for "+id); - final InputStream svgStream=database.getSVGLog(id); + final InputStream svgStream=database.getSVGLog(id,Monotone.HighlightTypes.AUTHORS); SAXSVGDocumentFactory factory=new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName()); final SVGDocument doc=factory.createSVGDocument("http://internal/graph",svgStream); SwingUtilities.invokeLater(new Runnable() { public void run() { @@ -588,7 +588,8 @@ JTextField property=new JTextField(id); property.setEditable(false); info.add(property,c); - GXLNode gxlNode=(GXLNode)database.log2gxl.gxlDocument.getElement(id); + GXLDocument doc=database.log2gxl.gxlDocument; + GXLNode gxlNode=(GXLNode)doc.getElement(id); addInfo(info,gxlNode,"Authors"); addInfo(info,gxlNode,"Branches"); addInfo(info,gxlNode,"Tags"); --- contrib/monotree/Log2Gxl.java +++ contrib/monotree/Log2Gxl.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.logging.Logger; +import java.util.logging.Level; import net.sourceforge.gxl.GXLDocument; import net.sourceforge.gxl.GXLGraph; import net.sourceforge.gxl.GXLNode; @@ -54,11 +55,12 @@ * Main method * Invoke via: monotone --db=database.db log id | java -classpath gxl.jar:. uk.co.srs.monotree.Log2Gxl | xslt gxl2dot.xsl >log.dot * or monotone --db=database.db log id | java -classpath gxl.jar:. uk.co.srs.monotree.Log2Gxl | xslt gxl2dot.xsl | dot -Tsvg >log.svg - * or monotone --db=database.db log id | java -classpath gxl.jar:. uk.co.srs.monotree.Log2Gxl --authorfile | xslt gxl2dot.xsl | dot -Tsvg >log.svg + * or monotone --db=database.db log id | java -classpath gxl.jar:. uk.co.srs.monotree.Log2Gxl --colorfile | xslt gxl2dot.xsl | dot -Tsvg >log.svg * - * @param argv command line arguments, --authorfile to specify a file mapping authors to colors + * @param argv command line arguments, --colorfile to specify a file mapping authors to colors */ public static void main(String argv[]) throws IOException,IllegalStateException,InterruptedException { + logger.setLevel(Level.FINEST); Log2Gxl processor=new Log2Gxl(); processor.start(argv); processor.join(); @@ -267,10 +269,10 @@ addToAttributeSet("Authors",author); if(colorAuthors) { - String color=authorColorMap.get(author); + String color=highlightColorMap.get(author); if(color==null) { - color=colors[authorColorMap.size()]; - authorColorMap.put(author,color); + color=colors[highlightColorMap.size()]; + highlightColorMap.put(author,color); } currentNode.setAttr("AuthorColor",new GXLString(color)); } @@ -287,6 +289,15 @@ String branch=line.substring("Branch:".length()+1); if(branch.length()!=0) { addToAttributeSet("Branches",branch); + + if(colorBranches) { + String color=highlightColorMap.get(branch); + if(color==null) { + color=colors[highlightColorMap.size()]; + highlightColorMap.put(branch,color); + } + currentNode.setAttr("BranchColor",new GXLString(color)); + } } } @@ -413,26 +424,31 @@ String line=source.readLine(); if(line.length()>0) throw new IOException(source.getLineNumber()+": Unexpected data ["+line+"]"); line=lookahead(); + if(line.startsWith("Deleted files:")) parseDeletedFiles(); + line=lookahead(); if(line.startsWith("Renamed files:")) parseRenamedFiles(); line=lookahead(); if(line.startsWith("Renamed directories:")) parseRenamedDirectories(); line=lookahead(); - if(line.startsWith("Deleted files:")) parseDeletedFiles(); - line=lookahead(); if(line.startsWith("Added files:")) parseAddedFiles(); line=lookahead(); if(line.startsWith("Modified files:")) parseModifiedFiles(); } /** - * If true, set the background color of the node to indicate the author + * If true, output a color of the node to indicate the author of the node */ private boolean colorAuthors=true; /** + * If true, output a color attribute to indicate the branch of the node + */ + private boolean colorBranches=true; + + /** * Map of author identifiers to allocated colors */ - private Map authorColorMap=new HashMap(); + private Map highlightColorMap=new HashMap(); /** * Array of color names used to allocate colors @@ -1155,36 +1171,36 @@ * This file should consist of lines like * address@hidden * - * @param authorFileName the name of the author file (may be qualified) + * @param colorfileName the name of the author file (may be qualified) * @throws IOException if there is a problem with the author to color mapping file */ - private void loadAuthorFile(String authorFileName) throws IOException { - Properties authorMap=new Properties(); - InputStream authorMapStream=null; + private void loadColorfile(String colorfileName) throws IOException { + Properties colorMap=new Properties(); + InputStream colorMapStream=null; try { - authorMapStream=new FileInputStream(authorFileName); - authorMap.load(authorMapStream); + colorMapStream=new FileInputStream(colorfileName); + colorMap.load(colorMapStream); List colorList=new ArrayList(Arrays.asList(colors)); - for(Object key: authorMap.keySet()) { - String color=(String)authorMap.get(key); + for(Object key: colorMap.keySet()) { + String color=(String)colorMap.get(key); if(!colorList.contains(color)) { - throw new IOException("Illegal color "+color+" in author color map file"); + throw new IOException("Illegal color "+color+" in color map file"); } - authorColorMap.put((String)key,color); + highlightColorMap.put((String)key,color); logger.config(key+"="+color); colorList.remove(color); } colors=colorList.toArray(new String[colorList.size()]); } finally { - if(authorMapStream!=null) authorMapStream.close(); + if(colorMapStream!=null) colorMapStream.close(); } } /** * Start parsing the output of a monotone log command and output the corresponding GXL graph * - * @param argv the command line arguments, --authorfile to specify a file mapping authors to colors + * @param argv the command line arguments, --colorfile to specify a file mapping authors to colors * @throws IOException if there is a read error on the input stream or the input stream runs dry * @throws IllegalStateException if the header lines aren't as expected */ @@ -1195,7 +1211,7 @@ /** * Start parsing the output of a monotone log command and output the corresponding GXL graph * - * @param argv the command line arguments, --authorfile to specify a file mapping authors to colors + * @param argv the command line arguments, --colorfile to specify a file mapping authors to colors * @throws IOException if there is a read error on the input stream or the input stream runs dry * @throws IllegalStateException if the header lines aren't as expected */ @@ -1203,10 +1219,10 @@ nodes=new HashMap(); if(argv.length>0) { for(int I=0;I"); + System.err.println("Usage: Log2Gxl --colorfile "); System.exit(-1); } } @@ -1226,6 +1242,7 @@ doRun(); } catch(Exception e) { + e.printStackTrace(); logger.throwing(this.getClass().getName(),"run",e); } } --- contrib/monotree/Makefile +++ contrib/monotree/Makefile @@ -1,14 +1,14 @@ all: monotree.jar -monotree.jar: Log2Gxl.class Monotone.class GXLViewer.class META-INF/MANIFEST.MF gxl2dot.xsl - jar cvmf META-INF/MANIFEST.MF monotree.jar Log2Gxl.class Monotone.class GXLViewer.class GXLViewer\$$1.class GXLViewer\$$2.class GXLViewer\$$3.class GXLViewer\$$4.class GXLViewer\$$5.class GXLViewer\$$6.class Monotone\$$ErrorReader.class Monotone\$$StreamCopier.class GXLViewer\$$ReadBranches.class GXLViewer\$$MonotoneFileFilter.class GXLViewer\$$ReadBranches\$$1.class GXLViewer\$$DisplayLog.class GXLViewer\$$DisplayLog\$$1.class GXLViewer\$$GXLUserAgent.class GXLViewer\$$7.class GXLViewer\$$OnClickAction.class gxl2dot.xsl Monotone\$$1.class GXLViewer\$$OnClickAction\$$1.class +monotree.jar: Log2Gxl.class Monotone.class GXLViewer.class META-INF/MANIFEST.MF gxl2dot.xsl gxl-1.0.dtd + jar cvmf META-INF/MANIFEST.MF monotree.jar Log2Gxl.class Monotone.class GXLViewer.class GXLViewer\$$1.class GXLViewer\$$2.class GXLViewer\$$3.class GXLViewer\$$4.class GXLViewer\$$5.class GXLViewer\$$6.class Monotone\$$ErrorReader.class Monotone\$$StreamCopier.class GXLViewer\$$ReadBranches.class GXLViewer\$$MonotoneFileFilter.class GXLViewer\$$ReadBranches\$$1.class GXLViewer\$$DisplayLog.class GXLViewer\$$DisplayLog\$$1.class GXLViewer\$$GXLUserAgent.class GXLViewer\$$7.class GXLViewer\$$OnClickAction.class gxl2dot.xsl Monotone\$$1.class GXLViewer\$$OnClickAction\$$1.class gxl-1.0.dtd Monotone\$$HighlightTypes.class InternalURIResolver.class Log2Gxl.class: Log2Gxl.java javac -g -classpath "gxl/gxl-0.92/gxl.jar;." Log2Gxl.java -Monotone.class Monotone$$ErrorReader.class Monotone$$StreamCopier.class Monotone$$1.class : Monotone.java Log2Gxl.class +Monotone.class Monotone$$ErrorReader.class Monotone$$StreamCopier.class Monotone$$1.class Monotone$$HighlightTypes.class InternalURIResolver.class: Monotone.java Log2Gxl.class javac -g Monotone.java GXLViewer$$OnClickAction.class GXLViewer$$ReadBranches.class GXLViewer$$2$$1.class GXLViewer$$DisplayLog.class GXLViewer$$1.class GXLViewer$$2.class GXLViewer$$3.class GXLViewer$$4.class GXLViewer$$5 GXLViewer$$6 GXLViewer.class GXLViewer$$MonotoneFileFilter.class GXLViewer$$ReadBranches$$1.class GXLViewer$$DisplayLog$$1.class GXLViewer$$GXLUserAgent.class GXLViewer$$7.class GXLViewer$$OnClickAction$$1.class: GXLViewer.java Monotone.class --- contrib/monotree/Monotone.java +++ contrib/monotree/Monotone.java @@ -20,8 +20,11 @@ import java.io.BufferedOutputStream; import java.io.BufferedInputStream; import java.util.logging.Logger; +import java.util.logging.Level; import javax.xml.transform.TransformerFactory; import javax.xml.transform.Transformer; +import javax.xml.transform.Source; +import javax.xml.transform.URIResolver; import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamResult; @@ -49,6 +52,7 @@ */ public Monotone(File database) { this.database=database; + logger.setLevel(Level.FINEST); } /** @@ -103,27 +107,36 @@ */ public Log2Gxl log2gxl; + public enum HighlightTypes { + NONE, + AUTHORS, + BRANCHES + }; + /** * Run monotone and get an SVG stream from a log * - * @param id the identifier for which the log should be generated + * @param id the identifier (revision or file) for which the log should be generated (not null) + * @param highlight an enum specifing the node background highlight type requested * @return a stream from which an SVG format graph may be read */ - public InputStream getSVGLog(String id) throws IOException { - String command="log "+id; + public InputStream getSVGLog(final String id,final HighlightTypes highlight) throws IOException { + final String command="log --revision "+id; // Start the inferior processes - Process monotone=Runtime.getRuntime().exec(getBaseCommand()+command); + final Process monotone=Runtime.getRuntime().exec(getBaseCommand()+command); new ErrorReader("monotone",monotone.getErrorStream()); - Process dot2svg=Runtime.getRuntime().exec("dot -Tsvg"); + final Process dot2svg=Runtime.getRuntime().exec("dot -Tsvg"); new ErrorReader("dot2svg",dot2svg.getErrorStream()); final PipedOutputStream gxl2dotSourceOutputStream=new PipedOutputStream(); final PipedInputStream gxl2dotSourceInputStream=new PipedInputStream(gxl2dotSourceOutputStream); // Chain the log output to the GXL generator and into the dot converter - String[] args=new String[] { "--authorfile","authors.map" }; - if(!(new File("authors.map")).exists()) args=new String[0]; + final String[] args; + if(!(new File("colors.map")).exists()) args=new String[0]; + else args=new String[] { "--colorfile","colors.map" }; + log2gxl=new Log2Gxl(); log2gxl.start(args,monotone.getInputStream(),gxl2dotSourceOutputStream); @@ -131,10 +144,12 @@ final PipedInputStream gxl2dotSinkInputStream=new PipedInputStream(gxl2dotSinkOutputStream); // Create a thread to transform the GXL semantic graph into an DOT visual graph - Thread transformerThread=new Thread(new Runnable() { public void run() { + final Thread transformerThread=new Thread(new Runnable() { public void run() { try { TransformerFactory factory=TransformerFactory.newInstance(); + factory.setURIResolver(new InternalURIResolver()); Transformer transformer=factory.newTransformer(new StreamSource(ClassLoader.getSystemResourceAsStream("gxl2dot.xsl"))); + transformer.setParameter("HIGHLIGHT",highlight.toString()); transformer.transform(new StreamSource(gxl2dotSourceInputStream),new StreamResult(gxl2dotSinkOutputStream)); } catch(Exception e) { @@ -297,3 +312,19 @@ } } } + +class InternalURIResolver implements URIResolver { + + /** + * Log sink + */ + private static Logger logger=Logger.getLogger("Monotone"); + + public Source resolve(String href,String base) { + logger.info("URI is "+href); + if(href.equals("http://www.gupro.de/GXL/gxl-1.0.dtd")) { + return new StreamSource(ClassLoader.getSystemResourceAsStream("gxl-1.0.dtd")); + } + return null; + } +} --- contrib/monotree/gxl-1.0.dtd +++ contrib/monotree/gxl-1.0.dtd @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +