package com.redhat.rcm.version.util; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; import static org.apache.commons.io.IOUtils.closeQuietly; import static org.apache.commons.lang.StringUtils.isEmpty; import static org.apache.commons.lang.StringUtils.join; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Stack; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.apache.maven.mae.project.key.FullProjectKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PomPeek { private static final String G = "g"; private static final String A = "a"; private static final String V = "v"; private static final String PG = "pg"; private static final String PA = "pa"; private static final String PV = "pv"; private static final String PKG = "pkg"; private static final String PRP = "prp"; private static final String[] COORD_KEYS = { G, A, V, PG, PA, PV, PKG, PRP }; private static final String MODULE_ELEM = "module"; private static final String MODULES_ELEM = "modules"; private static final Map<String, String> CAPTURED_PATHS = new HashMap<String, String>() { private static final long serialVersionUID = 1L; { put( "project:groupId", G ); put( "project:artifactId", A ); put( "project:version", V ); put( "project:packaging", PKG ); put( "project:parent:groupId", PG ); put( "project:parent:artifactId", PA ); put( "project:parent:version", PV ); put( "project:parent:relativePath", PRP ); } }; private final Logger logger = LoggerFactory.getLogger( getClass() ); private FullProjectKey key; private final Map<String, String> elementValues = new HashMap<String, String>(); private final Set<String> modules = new HashSet<String>(); private final File pom; private FullProjectKey parentKey; private boolean modulesDone = false; public PomPeek( final File pom ) { this.pom = pom; parseCoordElements( pom ); if ( !createCoordinateInfo() ) { logger.warn( "Could not peek at POM coordinate for: {}. " + "This POM will NOT be available as an ancestor to other models during effective-model building.", pom ); } } public String getParentRelativePath() { return elementValues.get( PRP ); } public Set<String> getModules() { return modules; } public File getPom() { return pom; } public FullProjectKey getKey() { return key; } public FullProjectKey getParentKey() { return parentKey; } private void parseCoordElements( final File pom ) { Reader reader = null; XMLStreamReader xml = null; try { reader = new FileReader( pom ); xml = XMLInputFactory.newFactory() .createXMLStreamReader( reader ); final Stack<String> path = new Stack<String>(); while ( xml.hasNext() ) { final int evt = xml.next(); switch ( evt ) { case START_ELEMENT: { final String elem = xml.getLocalName(); path.push( elem ); if ( captureValue( elem, path, xml ) ) { // seems like xml.getElementText() traverses the END_ELEMENT event... path.pop(); } break; } case END_ELEMENT: { final String elem = xml.getLocalName(); if ( MODULES_ELEM.equals( elem ) ) { modulesDone = true; } path.pop(); break; } default: { } } if ( foundAll() ) { return; } } } catch ( final IOException e ) { logger.warn( "Failed to peek at POM coordinate for: {}. Reason: {}\n" + "This POM will NOT be available as an ancestor to other models during effective-model building.", e, pom, e.getMessage() ); } catch ( final XMLStreamException e ) { logger.warn( "Failed to peek at POM coordinate for: {}. Reason: {}\n" + "This POM will NOT be available as an ancestor to other models during effective-model building.", e, pom, e.getMessage() ); } catch ( final FactoryConfigurationError e ) { logger.warn( "Failed to peek at POM coordinate for: {}. Reason: {}\n" + "This POM will NOT be available as an ancestor to other models during effective-model building.", e, pom, e.getMessage() ); } finally { if ( xml != null ) { try { xml.close(); } catch ( final XMLStreamException e ) { } } closeQuietly( reader ); } } private boolean foundAll() { for ( final String key : COORD_KEYS ) { if ( !elementValues.containsKey( key ) ) { return false; } } if ( "pom".equals( elementValues.get( PKG ) ) && !modulesDone ) { return false; } return true; } private boolean captureValue( final String elem, final Stack<String> path, final XMLStreamReader xml ) throws XMLStreamException { final String pathStr = join( path, ":" ); final String key = CAPTURED_PATHS.get( pathStr ); if ( key != null ) { elementValues.put( key, xml.getElementText() .trim() ); return true; } else if ( MODULE_ELEM.equals( elem ) ) { modules.add( xml.getElementText() .trim() ); } return false; } private boolean createCoordinateInfo() { String v = elementValues.get( V ); final String pv = elementValues.get( PV ); if ( isEmpty( v ) ) { v = pv; } String g = elementValues.get( G ); final String pg = elementValues.get( PG ); if ( isEmpty( g ) ) { g = pg; } final String a = elementValues.get( A ); final String pa = elementValues.get( PA ); boolean valid = false; if ( isValidArtifactId( a ) && isValidGroupId( g ) && isValidVersion( v ) ) { key = new FullProjectKey( g, a, v ); valid = true; } if ( isValidArtifactId( pa ) && isValidGroupId( pg ) && isValidVersion( pv ) ) { parentKey = new FullProjectKey( pg, pa, pv ); } return valid; } private boolean isValidVersion( final String version ) { if ( isEmpty( version ) ) { return false; } if ( "version".equals( version ) ) { return false; } if ( "parentVersion".equals( version ) ) { return false; } return true; } private boolean isValidGroupId( final String groupId ) { if ( isEmpty( groupId ) ) { return false; } if ( groupId.contains( "${" ) ) { return false; } if ( "parentGroupId".equals( groupId ) ) { return false; } if ( "groupId".equals( groupId ) ) { return false; } return true; } private boolean isValidArtifactId( final String artifactId ) { if ( isEmpty( artifactId ) ) { return false; } if ( artifactId.contains( "${" ) ) { return false; } if ( "parentArtifactId".equals( artifactId ) ) { return false; } if ( "artifactId".equals( artifactId ) ) { return false; } return true; } }