001package org.apache.maven.doxia.xsd;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.Reader;
026import java.net.JarURLConnection;
027import java.net.URL;
028import java.util.Collections;
029import java.util.Enumeration;
030import java.util.Hashtable;
031import java.util.Iterator;
032import java.util.List;
033import java.util.Locale;
034import java.util.Map;
035import java.util.jar.JarEntry;
036import java.util.jar.JarFile;
037
038import org.apache.maven.doxia.parser.AbstractXmlParser;
039
040import org.codehaus.plexus.util.FileUtils;
041import org.codehaus.plexus.util.IOUtil;
042import org.codehaus.plexus.util.ReaderFactory;
043import org.codehaus.plexus.util.SelectorUtils;
044import org.codehaus.plexus.util.xml.XmlUtil;
045
046import org.xml.sax.EntityResolver;
047
048/**
049 * Abstract class to validate XML files with DTD or XSD mainly for Doxia namespaces.
050 *
051 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
052 * @version $Id$
053 * @since 1.0
054 */
055public abstract class AbstractXmlValidatorTest
056    extends AbstractXmlValidator
057{
058
059    /** Simple cache mechanism to load test documents. */
060    private static final Map<String,String> CACHE_DOXIA_TEST_DOCUMENTS = new Hashtable<String,String>();
061
062    /** Maven resource in the doxia-test-docs-XXX.jar */
063    private static final String MAVEN_RESOURCE_PATH = "META-INF/maven/org.apache.maven.doxia/doxia-test-docs/";
064
065    // ----------------------------------------------------------------------
066    // Protected methods
067    // ----------------------------------------------------------------------
068
069    /**
070     * @return a non null patterns to includes specific test files.
071     * @see AbstractXmlValidatorTest#getTestDocuments()
072     */
073    protected abstract String[] getIncludes();
074
075    /**
076     * @return a map of test resources filtered by patterns from {@link #getIncludes()}.
077     * @throws IOException if any
078     * @see #getIncludes()
079     * @see #getAllTestDocuments()
080     */
081    protected Map<String, String> getTestDocuments()
082        throws IOException
083    {
084        if ( getIncludes() == null )
085        {
086            return Collections.emptyMap();
087        }
088
089        Map<String,String> testDocs = getAllTestDocuments();
090        Map<String,String> ret = new Hashtable<String,String>();
091        ret.putAll( testDocs );
092        for ( Iterator<String> it = testDocs.keySet().iterator(); it.hasNext(); )
093        {
094            String key = it.next();
095
096            for ( int i = 0; i < getIncludes().length; i++ )
097            {
098                if ( !SelectorUtils.matchPath( getIncludes()[i], key.toLowerCase( Locale.ENGLISH ) ) )
099                {
100                    ret.remove( key );
101                }
102            }
103        }
104
105        return ret;
106    }
107
108    /**
109     * Returns the EntityResolver that is used by the XMLReader for validation.
110     * By default a {@link AbstractXmlParser.CachedFileEntityResolver} is used,
111     * but implementations should override this for performance reasons.
112     *
113     * @return an EntityResolver. Not null.
114     *
115     * @since 1.2
116     */
117    protected EntityResolver getEntityResolver()
118    {
119        return new AbstractXmlParser.CachedFileEntityResolver();
120    }
121
122    /**
123     * Find test resources in the <code>doxia-test-docs-XXX.jar</code> or in an IDE project.
124     *
125     * @return a map of test resources defined as follow:
126     * <ul>
127     *   <li>key, the full url of test documents,
128     *      i.e. <code>jar:file:/.../doxia-test-docs-XXX.jar!/path/to/resource</code></li>
129     *   <li>value, the content for the resource defined by the key</li>
130     * </ul>
131     * @throws IOException if any
132     */
133    protected static Map<String,String> getAllTestDocuments()
134        throws IOException
135    {
136        if ( CACHE_DOXIA_TEST_DOCUMENTS != null && !CACHE_DOXIA_TEST_DOCUMENTS.isEmpty() )
137        {
138            return Collections.unmodifiableMap( CACHE_DOXIA_TEST_DOCUMENTS );
139        }
140
141        URL testJar = AbstractXmlValidatorTest.class.getClassLoader().getResource( MAVEN_RESOURCE_PATH );
142        if ( testJar == null )
143        {
144            // maybe in an IDE project
145            testJar = AbstractXmlValidatorTest.class.getClassLoader().getResource( "doxia-site" );
146
147            if ( testJar == null )
148            {
149                throw new RuntimeException(
150                        "Could not find the Doxia test documents artefact i.e. doxia-test-docs-XXX.jar" );
151            }
152        }
153
154        if ( testJar.toString().startsWith( "jar" ))
155            {
156            JarURLConnection conn = (JarURLConnection) testJar.openConnection();
157            JarFile jarFile = conn.getJarFile();
158            for ( Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); )
159            {
160                JarEntry entry = e.nextElement();
161
162                if ( entry.getName().startsWith( "META-INF" ) )
163                {
164                    continue;
165                }
166                if ( entry.isDirectory() )
167                {
168                    continue;
169                }
170
171                InputStream in = null;
172                try
173                {
174                    in = AbstractXmlValidatorTest.class.getClassLoader().getResource( entry.getName() ).openStream();
175                    String content = IOUtil.toString( in, "UTF-8" );
176                    CACHE_DOXIA_TEST_DOCUMENTS.put( "jar:" + conn.getJarFileURL() + "!/" + entry.getName(), content );
177                }
178                finally
179                {
180                    IOUtil.close( in );
181                }
182            }
183        }
184        else
185        {
186            // IDE projects
187            File testDocsDir = FileUtils.toFile( testJar ).getParentFile();
188
189            List<File> files = FileUtils.getFiles( testDocsDir, "**/*.*", FileUtils.getDefaultExcludesAsString(), true );
190            for ( Iterator<File> it = files.iterator(); it.hasNext();)
191            {
192                File file = new File( it.next().toString() );
193
194                if ( file.getAbsolutePath().contains( "META-INF" ) )
195                {
196                    continue;
197                }
198
199                Reader reader = null;
200                if ( XmlUtil.isXml( file ))
201                {
202                    reader = ReaderFactory.newXmlReader( file );
203                }
204                else
205                {
206                    reader = ReaderFactory.newReader( file, "UTF-8" );
207                }
208
209                String content = IOUtil.toString( reader );
210                CACHE_DOXIA_TEST_DOCUMENTS.put( file.toURI().toString(), content );
211            }
212        }
213
214        return Collections.unmodifiableMap( CACHE_DOXIA_TEST_DOCUMENTS );
215    }
216}