Project: arquillian-rusheye
/**
 * JBoss, Home of Professional Open Source 
 * Copyright ${year}, Red Hat, Inc. and individual contributors 
 * by the @authors tag. See the copyright.txt in the distribution for a 
 * full listing of individual contributors. 
 * 
 * This is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation; either version 2.1 of 
 * the License, or (at your option) any later version. 
 * 
 * This software is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * Lesser General Public License for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this software; if not, write to the Free 
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 
 */
package org.jboss.rusheye.parser; 
 
import java.io.BufferedInputStream; 
import java.io.BufferedOutputStream; 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.net.URL; 
import java.util.LinkedHashSet; 
import java.util.Set; 
 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.StreamFilter; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLStreamException; 
import javax.xml.stream.XMLStreamReader; 
 
import org.apache.commons.io.FileUtils; 
import org.codehaus.stax2.XMLInputFactory2; 
import org.codehaus.stax2.XMLStreamReader2; 
import org.codehaus.stax2.ri.Stax2FilteredStreamReader; 
import org.codehaus.stax2.validation.XMLValidationSchema; 
import org.codehaus.stax2.validation.XMLValidationSchemaFactory; 
import org.jboss.rusheye.exception.ConfigurationException; 
import org.jboss.rusheye.exception.ConfigurationValidationException; 
import org.jboss.rusheye.exception.ParsingException; 
import org.jboss.rusheye.listener.SuiteListenerAdapter; 
import org.jboss.rusheye.listener.SuiteListener; 
import org.jboss.rusheye.suite.GlobalConfiguration; 
import org.jboss.rusheye.suite.Mask; 
import org.jboss.rusheye.suite.Pattern; 
import org.jboss.rusheye.suite.Properties; 
import org.jboss.rusheye.suite.Test; 
import org.jboss.rusheye.suite.VisualSuite; 
 
import com.ctc.wstx.exc.WstxParsingException; 
import com.ctc.wstx.exc.WstxValidationException; 
 
/**
 * @author <a href="mailto:[email protected]">Lukas Fryc</a> 
 * @version $Revision$ 
 */
 
public final class Parser { 
 
    private Set<SuiteListener> listeners = new LinkedHashSet<SuiteListener>(); 
    private Handler handler = new Handler(listeners); 
    private Properties properties; 
 
    public Parser() { 
        this.registerListener(new ParserListenerRegistrationListener()); 
    } 
 
    public void parseResource(String resourceName) { 
        InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourceName); 
        parseStream(inputStream); 
    } 
 
    public void parseStream(InputStream inputStream) { 
        try { 
            File tmp = File.createTempFile(Parser.class.getName(), ".tmp"); 
            BufferedInputStream in = new BufferedInputStream(inputStream); 
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tmp)); 
 
            byte[] buf = new byte[1024]; 
            int len; 
            while ((len = inputStream.read(buf)) > 0) { 
                out.write(buf, 0, len); 
            } 
            out.close(); 
            in.close(); 
            parseFileTempFile(tmp); 
        } catch (IOException e) { 
            throw new ParsingException(e); 
        } 
    } 
 
    public void parseFile(File file) { 
        parseFile(file, false); 
    } 
 
    public void parseFileTempFile(File file) { 
        parseFile(file, true); 
    } 
 
    private void parseFile(File file, boolean tmpfile) { 
        VisualSuite visualSuite = null
        try { 
            XMLValidationSchemaFactory schemaFactory = XMLValidationSchemaFactory 
                .newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA); 
            URL schemaURL = getClass().getClassLoader().getResource("org/jboss/rusheye/visual-suite.xsd"); 
            XMLValidationSchema schema = schemaFactory.createSchema(schemaURL); 
 
            XMLInputFactory2 factory = (XMLInputFactory2) XMLInputFactory.newInstance(); 
 
            StreamFilter filter = new StreamFilter() { 
                @Override 
                public boolean accept(XMLStreamReader reader) { 
                    return reader.isStartElement(); 
                } 
            }; 
 
            XMLStreamReader2 reader = factory.createXMLStreamReader(file); 
            XMLStreamReader2 filteredReader = new Stax2FilteredStreamReader(reader, filter); 
 
            reader.validateAgainst(schema); 
 
            // EventFilter filter = new EventFilter() { 
            // @Override 
            // public boolean accept(XMLEvent reader) { 
            // return reader.isStartElement(); 
            // } 
            // }; 
            // 
            // XMLEventReader reader = factory.createXMLEventReader(file); 
            // XMLEventReader filteredReader = factory.createFilteredReader(reader, filter); 
 
            JAXBContext ctx = JAXBContext.newInstance(VisualSuite.class.getPackage().getName()); 
            Unmarshaller um = ctx.createUnmarshaller(); 
 
            UnmarshallerMultiListener listener = new UnmarshallerMultiListener(); 
            um.setListener(listener); 
 
            // skip parsing of the first element - visual-suite 
            filteredReader.nextTag(); 
 
            visualSuite = new VisualSuite(); 
            handler.setVisualSuite(visualSuite); 
            handler.getContext().invokeListeners().onSuiteStarted(visualSuite); 
 
            listener.registerListener(new UniqueIdentityChecker(handler.getContext())); 
 
            while (filteredReader.hasNext()) { 
                try { 
                    // go on the start of the next tag 
                    filteredReader.nextTag(); 
 
                    Object o = um.unmarshal(reader); 
                    if (o instanceof GlobalConfiguration) { 
                        GlobalConfiguration globalConfiguration = (GlobalConfiguration) o; 
                        handler.getContext().setCurrentConfiguration(globalConfiguration); 
                        visualSuite.setGlobalConfiguration(globalConfiguration); 
                        handler.getContext().invokeListeners().onConfigurationReady(visualSuite); 
 
                        RetriverInjector retriverInjector = new RetriverInjector(this); 
                        for (Mask mask : globalConfiguration.getMasks()) { 
                            retriverInjector.afterUnmarshal(mask, null); 
                        } 
                        listener.registerListener(retriverInjector); 
                    } 
                    if (o instanceof Test) { 
                        Test test = (Test) o; 
                        handler.getContext().setCurrentConfiguration(test); 
                        handler.getContext().setCurrentTest(test); 
                        for (Pattern pattern : test.getPatterns()) { 
                            handler.getContext().invokeListeners().onPatternReady(test, pattern); 
                        } 
                        Test testWrapped = ConfigurationCompiler.wrap(test, visualSuite.getGlobalConfiguration()); 
                        handler.getContext().invokeListeners().onTestReady(testWrapped); 
                    } 
                } catch (WstxParsingException e) { 
                    // intentionally blank - wrong end of document detection 
                } 
 
            } 
        } catch (XMLStreamException e) { 
            throw handleParsingException(e, e); 
        } catch (JAXBException e) { 
            throw handleParsingException(e, e.getLinkedException()); 
        } finally { 
            if (visualSuite != null && handler.getContext() != null) { 
                handler.getContext().invokeListeners().onSuiteReady(visualSuite); 
            } 
            if (tmpfile) { 
                FileUtils.deleteQuietly(file); 
            } 
        } 
    } 
 
    private RuntimeException handleParsingException(Throwable originalException, Throwable cause) { 
        if (cause != null && cause instanceof WstxValidationException) { 
            String message = cause.getMessage().replaceAll("\n"""); 
            return new ConfigurationValidationException(message, cause); 
        } 
        return new ParsingException(originalException); 
    } 
 
    public void registerListener(SuiteListener parserListener) { 
        synchronized (parserListener) { 
            if (listeners.contains(parserListener)) { 
                return
            } 
            listeners.add(parserListener); 
        } 
    } 
 
    public void unregisterListener(SuiteListener parserListener) { 
        synchronized (listeners) { 
            if (listeners.contains(parserListener)) { 
                listeners.remove(parserListener); 
            } 
        } 
        throw new IllegalStateException("Given parser isn't registered"); 
    } 
 
    Handler getHandler() { 
        return handler; 
    } 
 
    public void setProperties(Properties properties) { 
        this.properties = properties; 
    } 
 
    private class ParserListenerRegistrationListener extends SuiteListenerAdapter { 
        @Override 
        public void onConfigurationReady(VisualSuite visualSuite) { 
            for (SuiteListener listener : visualSuite.getGlobalConfiguration().getListeners()) { 
                if (Parser.this.properties != null) { 
                    listener.setProperties(Parser.this.properties); 
                } 
                listener.onSuiteStarted(visualSuite); 
                listener.onConfigurationReady(visualSuite); 
                Parser.this.registerListener(listener); 
            } 
 
            if (listeners.size() == 1) { 
                throw new ConfigurationException("No ParserListener was registered to process parsed tests"); 
            } 
        } 
    } 
}