git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270711 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -1,255 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.metamata; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.OutputStream; | |||
| import java.io.OutputStreamWriter; | |||
| import java.io.Writer; | |||
| import java.util.ArrayList; | |||
| import java.util.Hashtable; | |||
| import java.util.Iterator; | |||
| import javax.xml.parsers.DocumentBuilder; | |||
| import javax.xml.parsers.DocumentBuilderFactory; | |||
| import javax.xml.parsers.ParserConfigurationException; | |||
| import org.apache.avalon.framework.logger.AbstractLogEnabled; | |||
| import org.apache.myrmidon.api.TaskException; | |||
| import org.apache.aut.nativelib.ExecOutputHandler; | |||
| import org.apache.tools.ant.taskdefs.exec.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.util.DOMElementWriter; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcher; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcherFactory; | |||
| import org.w3c.dom.Document; | |||
| import org.w3c.dom.Element; | |||
| /** | |||
| * This is a very bad stream handler for the MAudit task. All report to stdout | |||
| * that does not match a specific report pattern is dumped to the Ant output as | |||
| * warn level. The report that match the pattern is stored in a map with the key | |||
| * being the filepath that caused the error report. <p> | |||
| * | |||
| * The limitation with the choosen implementation is clear: | |||
| * <ul> | |||
| * <li> it does not handle multiline report( message that has \n ). the part | |||
| * until the \n will be stored and the other part (which will not match the | |||
| * pattern) will go to Ant output in Warn level. | |||
| * <li> it does not report error that goes to stderr. | |||
| * </ul> | |||
| * | |||
| * | |||
| * @author <a href="sbailliez@imediation.com">Stephane Bailliez</a> | |||
| */ | |||
| class MAuditStreamHandler | |||
| extends AbstractLogEnabled | |||
| implements ExecuteStreamHandler, ExecOutputHandler | |||
| { | |||
| public void setProcessInputStream( OutputStream os ) | |||
| throws IOException | |||
| { | |||
| } | |||
| public void setProcessErrorStream( InputStream is ) | |||
| throws IOException | |||
| { | |||
| } | |||
| public void setProcessOutputStream( InputStream is ) | |||
| throws TaskException, IOException | |||
| { | |||
| } | |||
| public void start() | |||
| throws IOException | |||
| { | |||
| } | |||
| /** | |||
| * this is where the XML output will go, should mostly be a file the caller | |||
| * is responsible for flushing and closing this stream | |||
| */ | |||
| private OutputStream m_xmlOut; | |||
| /** | |||
| * the multimap. The key in the map is the filepath that caused the audit | |||
| * error and the value is a vector of MAudit.Violation entries. | |||
| */ | |||
| private Hashtable m_auditedFiles = new Hashtable(); | |||
| /** | |||
| * matcher that will be used to extract the info from the line | |||
| */ | |||
| private RegexpMatcher m_matcher; | |||
| private Hashtable m_fileMapping; | |||
| MAuditStreamHandler( Hashtable fileMapping, OutputStream xmlOut ) | |||
| throws TaskException | |||
| { | |||
| m_fileMapping = fileMapping; | |||
| m_xmlOut = xmlOut; | |||
| /** | |||
| * the matcher should be the Oro one. I don't know about the other one | |||
| */ | |||
| m_matcher = ( new RegexpMatcherFactory() ).newRegexpMatcher(); | |||
| m_matcher.setPattern( MAudit.AUDIT_PATTERN ); | |||
| } | |||
| private static final DocumentBuilder getDocumentBuilder() | |||
| { | |||
| try | |||
| { | |||
| return DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||
| } | |||
| catch( ParserConfigurationException pce ) | |||
| { | |||
| throw new ExceptionInInitializerError( pce ); | |||
| } | |||
| } | |||
| /** | |||
| * Pretty dangerous business here. It serializes what was extracted from the | |||
| * MAudit output and write it to the output. | |||
| */ | |||
| public void stop() | |||
| { | |||
| // serialize the content as XML, move this to another method | |||
| // this is the only code that could be needed to be overrided | |||
| Document doc = getDocumentBuilder().newDocument(); | |||
| Element rootElement = doc.createElement( "classes" ); | |||
| final Iterator keys = m_auditedFiles.keySet().iterator(); | |||
| rootElement.setAttribute( "audited", String.valueOf( m_fileMapping.size() ) ); | |||
| rootElement.setAttribute( "reported", String.valueOf( m_auditedFiles.size() ) ); | |||
| int errors = 0; | |||
| while( keys.hasNext() ) | |||
| { | |||
| String filepath = (String)keys.next(); | |||
| ArrayList v = (ArrayList)m_auditedFiles.get( filepath ); | |||
| String fullclassname = (String)m_fileMapping.get( filepath ); | |||
| if( fullclassname == null ) | |||
| { | |||
| getLogger().warn( "Could not find class mapping for " + filepath ); | |||
| continue; | |||
| } | |||
| int pos = fullclassname.lastIndexOf( '.' ); | |||
| String pkg = ( pos == -1 ) ? "" : fullclassname.substring( 0, pos ); | |||
| String clazzname = ( pos == -1 ) ? fullclassname : fullclassname.substring( pos + 1 ); | |||
| Element clazz = doc.createElement( "class" ); | |||
| clazz.setAttribute( "package", pkg ); | |||
| clazz.setAttribute( "name", clazzname ); | |||
| clazz.setAttribute( "violations", String.valueOf( v.size() ) ); | |||
| errors += v.size(); | |||
| for( int i = 0; i < v.size(); i++ ) | |||
| { | |||
| Violation violation = (Violation)v.get( i ); | |||
| Element error = doc.createElement( "violation" ); | |||
| error.setAttribute( "line", String.valueOf( violation.getLine() ) ); | |||
| error.setAttribute( "message", violation.getError() ); | |||
| clazz.appendChild( error ); | |||
| } | |||
| rootElement.appendChild( clazz ); | |||
| } | |||
| rootElement.setAttribute( "violations", String.valueOf( errors ) ); | |||
| // now write it to the outputstream, not very nice code | |||
| if( m_xmlOut != null ) | |||
| { | |||
| Writer wri = null; | |||
| try | |||
| { | |||
| wri = new OutputStreamWriter( m_xmlOut, "UTF-8" ); | |||
| wri.write( "<?xml version=\"1.0\"?>\n" ); | |||
| ( new DOMElementWriter() ).write( rootElement, wri, 0, " " ); | |||
| wri.flush(); | |||
| } | |||
| catch( IOException exc ) | |||
| { | |||
| getLogger().error( "Unable to write log file" ); | |||
| } | |||
| finally | |||
| { | |||
| if( m_xmlOut != System.out && m_xmlOut != System.err ) | |||
| { | |||
| if( wri != null ) | |||
| { | |||
| try | |||
| { | |||
| wri.close(); | |||
| } | |||
| catch( IOException e ) | |||
| { | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * add a violation entry for the file | |||
| */ | |||
| protected void addViolationEntry( String file, Violation entry ) | |||
| { | |||
| ArrayList violations = (ArrayList)m_auditedFiles.get( file ); | |||
| if( violations == null ) | |||
| { | |||
| // if there is no decl for this file yet, create it. | |||
| violations = new ArrayList(); | |||
| m_auditedFiles.put( file, violations ); | |||
| } | |||
| violations.add( entry ); | |||
| } | |||
| /** | |||
| * Receive notification about the process writing | |||
| * to standard error. | |||
| */ | |||
| public void stderr( String line ) | |||
| { | |||
| } | |||
| /** | |||
| * Receive notification about the process writing | |||
| * to standard output. | |||
| */ | |||
| public void stdout( final String line ) | |||
| { | |||
| // we suppose here that there is only one report / line. | |||
| // There will obviouslly be a problem if the message is on several lines... | |||
| final ArrayList matches = getGroups( line ); | |||
| if( matches != null ) | |||
| { | |||
| final String file = (String)matches.get( 1 ); | |||
| final int lineNum = Integer.parseInt( (String)matches.get( 2 ) ); | |||
| final String msg = (String)matches.get( 3 ); | |||
| final Violation violation = new Violation( msg, lineNum ); | |||
| addViolationEntry( file, violation ); | |||
| } | |||
| else | |||
| { | |||
| // this doesn't match..report it as info, it could be | |||
| // either the copyright, summary or a multiline message (damn !) | |||
| getLogger().info( line ); | |||
| } | |||
| } | |||
| private ArrayList getGroups( final String line ) | |||
| { | |||
| try | |||
| { | |||
| return m_matcher.getGroups( line ); | |||
| } | |||
| catch( final TaskException te ) | |||
| { | |||
| getLogger().error( "Failed to process matcher", te ); | |||
| return new ArrayList(); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,255 +0,0 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.metamata; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.OutputStream; | |||
| import java.io.OutputStreamWriter; | |||
| import java.io.Writer; | |||
| import java.util.ArrayList; | |||
| import java.util.Hashtable; | |||
| import java.util.Iterator; | |||
| import javax.xml.parsers.DocumentBuilder; | |||
| import javax.xml.parsers.DocumentBuilderFactory; | |||
| import javax.xml.parsers.ParserConfigurationException; | |||
| import org.apache.avalon.framework.logger.AbstractLogEnabled; | |||
| import org.apache.myrmidon.api.TaskException; | |||
| import org.apache.aut.nativelib.ExecOutputHandler; | |||
| import org.apache.tools.ant.taskdefs.exec.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.util.DOMElementWriter; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcher; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcherFactory; | |||
| import org.w3c.dom.Document; | |||
| import org.w3c.dom.Element; | |||
| /** | |||
| * This is a very bad stream handler for the MAudit task. All report to stdout | |||
| * that does not match a specific report pattern is dumped to the Ant output as | |||
| * warn level. The report that match the pattern is stored in a map with the key | |||
| * being the filepath that caused the error report. <p> | |||
| * | |||
| * The limitation with the choosen implementation is clear: | |||
| * <ul> | |||
| * <li> it does not handle multiline report( message that has \n ). the part | |||
| * until the \n will be stored and the other part (which will not match the | |||
| * pattern) will go to Ant output in Warn level. | |||
| * <li> it does not report error that goes to stderr. | |||
| * </ul> | |||
| * | |||
| * | |||
| * @author <a href="sbailliez@imediation.com">Stephane Bailliez</a> | |||
| */ | |||
| class MAuditStreamHandler | |||
| extends AbstractLogEnabled | |||
| implements ExecuteStreamHandler, ExecOutputHandler | |||
| { | |||
| public void setProcessInputStream( OutputStream os ) | |||
| throws IOException | |||
| { | |||
| } | |||
| public void setProcessErrorStream( InputStream is ) | |||
| throws IOException | |||
| { | |||
| } | |||
| public void setProcessOutputStream( InputStream is ) | |||
| throws TaskException, IOException | |||
| { | |||
| } | |||
| public void start() | |||
| throws IOException | |||
| { | |||
| } | |||
| /** | |||
| * this is where the XML output will go, should mostly be a file the caller | |||
| * is responsible for flushing and closing this stream | |||
| */ | |||
| private OutputStream m_xmlOut; | |||
| /** | |||
| * the multimap. The key in the map is the filepath that caused the audit | |||
| * error and the value is a vector of MAudit.Violation entries. | |||
| */ | |||
| private Hashtable m_auditedFiles = new Hashtable(); | |||
| /** | |||
| * matcher that will be used to extract the info from the line | |||
| */ | |||
| private RegexpMatcher m_matcher; | |||
| private Hashtable m_fileMapping; | |||
| MAuditStreamHandler( Hashtable fileMapping, OutputStream xmlOut ) | |||
| throws TaskException | |||
| { | |||
| m_fileMapping = fileMapping; | |||
| m_xmlOut = xmlOut; | |||
| /** | |||
| * the matcher should be the Oro one. I don't know about the other one | |||
| */ | |||
| m_matcher = ( new RegexpMatcherFactory() ).newRegexpMatcher(); | |||
| m_matcher.setPattern( MAudit.AUDIT_PATTERN ); | |||
| } | |||
| private static final DocumentBuilder getDocumentBuilder() | |||
| { | |||
| try | |||
| { | |||
| return DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||
| } | |||
| catch( ParserConfigurationException pce ) | |||
| { | |||
| throw new ExceptionInInitializerError( pce ); | |||
| } | |||
| } | |||
| /** | |||
| * Pretty dangerous business here. It serializes what was extracted from the | |||
| * MAudit output and write it to the output. | |||
| */ | |||
| public void stop() | |||
| { | |||
| // serialize the content as XML, move this to another method | |||
| // this is the only code that could be needed to be overrided | |||
| Document doc = getDocumentBuilder().newDocument(); | |||
| Element rootElement = doc.createElement( "classes" ); | |||
| final Iterator keys = m_auditedFiles.keySet().iterator(); | |||
| rootElement.setAttribute( "audited", String.valueOf( m_fileMapping.size() ) ); | |||
| rootElement.setAttribute( "reported", String.valueOf( m_auditedFiles.size() ) ); | |||
| int errors = 0; | |||
| while( keys.hasNext() ) | |||
| { | |||
| String filepath = (String)keys.next(); | |||
| ArrayList v = (ArrayList)m_auditedFiles.get( filepath ); | |||
| String fullclassname = (String)m_fileMapping.get( filepath ); | |||
| if( fullclassname == null ) | |||
| { | |||
| getLogger().warn( "Could not find class mapping for " + filepath ); | |||
| continue; | |||
| } | |||
| int pos = fullclassname.lastIndexOf( '.' ); | |||
| String pkg = ( pos == -1 ) ? "" : fullclassname.substring( 0, pos ); | |||
| String clazzname = ( pos == -1 ) ? fullclassname : fullclassname.substring( pos + 1 ); | |||
| Element clazz = doc.createElement( "class" ); | |||
| clazz.setAttribute( "package", pkg ); | |||
| clazz.setAttribute( "name", clazzname ); | |||
| clazz.setAttribute( "violations", String.valueOf( v.size() ) ); | |||
| errors += v.size(); | |||
| for( int i = 0; i < v.size(); i++ ) | |||
| { | |||
| Violation violation = (Violation)v.get( i ); | |||
| Element error = doc.createElement( "violation" ); | |||
| error.setAttribute( "line", String.valueOf( violation.getLine() ) ); | |||
| error.setAttribute( "message", violation.getError() ); | |||
| clazz.appendChild( error ); | |||
| } | |||
| rootElement.appendChild( clazz ); | |||
| } | |||
| rootElement.setAttribute( "violations", String.valueOf( errors ) ); | |||
| // now write it to the outputstream, not very nice code | |||
| if( m_xmlOut != null ) | |||
| { | |||
| Writer wri = null; | |||
| try | |||
| { | |||
| wri = new OutputStreamWriter( m_xmlOut, "UTF-8" ); | |||
| wri.write( "<?xml version=\"1.0\"?>\n" ); | |||
| ( new DOMElementWriter() ).write( rootElement, wri, 0, " " ); | |||
| wri.flush(); | |||
| } | |||
| catch( IOException exc ) | |||
| { | |||
| getLogger().error( "Unable to write log file" ); | |||
| } | |||
| finally | |||
| { | |||
| if( m_xmlOut != System.out && m_xmlOut != System.err ) | |||
| { | |||
| if( wri != null ) | |||
| { | |||
| try | |||
| { | |||
| wri.close(); | |||
| } | |||
| catch( IOException e ) | |||
| { | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * add a violation entry for the file | |||
| */ | |||
| protected void addViolationEntry( String file, Violation entry ) | |||
| { | |||
| ArrayList violations = (ArrayList)m_auditedFiles.get( file ); | |||
| if( violations == null ) | |||
| { | |||
| // if there is no decl for this file yet, create it. | |||
| violations = new ArrayList(); | |||
| m_auditedFiles.put( file, violations ); | |||
| } | |||
| violations.add( entry ); | |||
| } | |||
| /** | |||
| * Receive notification about the process writing | |||
| * to standard error. | |||
| */ | |||
| public void stderr( String line ) | |||
| { | |||
| } | |||
| /** | |||
| * Receive notification about the process writing | |||
| * to standard output. | |||
| */ | |||
| public void stdout( final String line ) | |||
| { | |||
| // we suppose here that there is only one report / line. | |||
| // There will obviouslly be a problem if the message is on several lines... | |||
| final ArrayList matches = getGroups( line ); | |||
| if( matches != null ) | |||
| { | |||
| final String file = (String)matches.get( 1 ); | |||
| final int lineNum = Integer.parseInt( (String)matches.get( 2 ) ); | |||
| final String msg = (String)matches.get( 3 ); | |||
| final Violation violation = new Violation( msg, lineNum ); | |||
| addViolationEntry( file, violation ); | |||
| } | |||
| else | |||
| { | |||
| // this doesn't match..report it as info, it could be | |||
| // either the copyright, summary or a multiline message (damn !) | |||
| getLogger().info( line ); | |||
| } | |||
| } | |||
| private ArrayList getGroups( final String line ) | |||
| { | |||
| try | |||
| { | |||
| return m_matcher.getGroups( line ); | |||
| } | |||
| catch( final TaskException te ) | |||
| { | |||
| getLogger().error( "Failed to process matcher", te ); | |||
| return new ArrayList(); | |||
| } | |||
| } | |||
| } | |||