View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.doxia.xsd;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.Reader;
25  import java.net.JarURLConnection;
26  import java.net.URL;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.Enumeration;
30  import java.util.Hashtable;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.jar.JarEntry;
34  import java.util.jar.JarFile;
35  
36  import org.apache.commons.io.FileUtils;
37  import org.apache.commons.io.IOUtils;
38  import org.apache.commons.io.filefilter.TrueFileFilter;
39  import org.apache.maven.doxia.parser.AbstractXmlParser;
40  import org.codehaus.plexus.util.ReaderFactory;
41  import org.codehaus.plexus.util.SelectorUtils;
42  import org.codehaus.plexus.util.xml.XmlUtil;
43  import org.xml.sax.EntityResolver;
44  
45  /**
46   * Abstract class to validate XML files with DTD or XSD mainly for Doxia namespaces.
47   *
48   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
49   * @since 1.0
50   */
51  public abstract class AbstractXmlValidatorTest extends AbstractXmlValidator {
52  
53      /** Simple cache mechanism to load test documents. */
54      private static final Map<String, String> CACHE_DOXIA_TEST_DOCUMENTS = new Hashtable<>();
55  
56      /** Maven resource in the doxia-test-docs-XXX.jar */
57      private static final String MAVEN_RESOURCE_PATH = "META-INF/maven/org.apache.maven.doxia/doxia-test-docs/";
58  
59      // ----------------------------------------------------------------------
60      // Protected methods
61      // ----------------------------------------------------------------------
62  
63      /**
64       * @return a non null patterns to includes specific test files.
65       * @see AbstractXmlValidatorTest#getTestDocuments()
66       */
67      protected abstract String[] getIncludes();
68  
69      /**
70       * @return a map of test resources filtered by patterns from {@link #getIncludes()}.
71       * @throws IOException if any
72       * @see #getIncludes()
73       * @see #getAllTestDocuments()
74       */
75      protected Map<String, String> getTestDocuments() throws IOException {
76          if (getIncludes() == null) {
77              return Collections.emptyMap();
78          }
79  
80          Map<String, String> testDocs = getAllTestDocuments();
81          Map<String, String> ret = new Hashtable<>(testDocs);
82          for (String key : testDocs.keySet()) {
83              for (int i = 0; i < getIncludes().length; i++) {
84                  if (!SelectorUtils.matchPath(getIncludes()[i], key.toLowerCase(Locale.ENGLISH))) {
85                      ret.remove(key);
86                  }
87              }
88          }
89  
90          return ret;
91      }
92  
93      /**
94       * Returns the EntityResolver that is used by the XMLReader for validation.
95       * By default a {@link AbstractXmlParser.CachedFileEntityResolver} is used,
96       * but implementations should override this for performance reasons.
97       *
98       * @return an EntityResolver. Not null.
99       *
100      * @since 1.2
101      */
102     protected EntityResolver getEntityResolver() {
103         return new AbstractXmlParser.CachedFileEntityResolver();
104     }
105 
106     /**
107      * Find test resources in the <code>doxia-test-docs-XXX.jar</code> or in an IDE project.
108      *
109      * @return a map of test resources defined as follow:
110      * <ul>
111      *   <li>key, the full url of test documents,
112      *      i.e. <code>jar:file:/.../doxia-test-docs-XXX.jar!/path/to/resource</code></li>
113      *   <li>value, the content for the resource defined by the key</li>
114      * </ul>
115      * @throws IOException if any
116      */
117     protected static Map<String, String> getAllTestDocuments() throws IOException {
118         if (CACHE_DOXIA_TEST_DOCUMENTS != null && !CACHE_DOXIA_TEST_DOCUMENTS.isEmpty()) {
119             return Collections.unmodifiableMap(CACHE_DOXIA_TEST_DOCUMENTS);
120         }
121 
122         URL testJar = AbstractXmlValidatorTest.class.getClassLoader().getResource(MAVEN_RESOURCE_PATH);
123         if (testJar == null) {
124             // maybe in an IDE project
125             testJar = AbstractXmlValidatorTest.class.getClassLoader().getResource("doxia-site");
126 
127             if (testJar == null) {
128                 throw new IllegalStateException(
129                         "Could not find the Doxia test documents artefact i.e. doxia-test-docs-XXX.jar");
130             }
131         }
132 
133         if (testJar.toString().startsWith("jar")) {
134             JarURLConnection conn = (JarURLConnection) testJar.openConnection();
135             JarFile jarFile = conn.getJarFile();
136             for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
137                 JarEntry entry = e.nextElement();
138 
139                 if (entry.getName().startsWith("META-INF")) {
140                     continue;
141                 }
142                 if (entry.isDirectory()) {
143                     continue;
144                 }
145 
146                 try (InputStream in = AbstractXmlValidatorTest.class
147                         .getClassLoader()
148                         .getResource(entry.getName())
149                         .openStream()) {
150                     String content = IOUtils.toString(in, "UTF-8");
151                     CACHE_DOXIA_TEST_DOCUMENTS.put("jar:" + conn.getJarFileURL() + "!/" + entry.getName(), content);
152                 }
153             }
154         } else {
155             // IDE projects
156             File testDocsDir = FileUtils.toFile(testJar).getParentFile();
157 
158             Collection<File> files = FileUtils.listFiles(testDocsDir, TrueFileFilter.TRUE, TrueFileFilter.TRUE);
159             for (File file1 : files) {
160                 File file = new File(file1.toString());
161 
162                 if (file.getAbsolutePath().contains("META-INF")) {
163                     continue;
164                 }
165 
166                 Reader reader;
167                 if (XmlUtil.isXml(file)) {
168                     reader = ReaderFactory.newXmlReader(file);
169                 } else {
170                     reader = ReaderFactory.newReader(file, "UTF-8");
171                 }
172 
173                 String content = IOUtils.toString(reader);
174                 CACHE_DOXIA_TEST_DOCUMENTS.put(file.toURI().toString(), content);
175             }
176         }
177 
178         return Collections.unmodifiableMap(CACHE_DOXIA_TEST_DOCUMENTS);
179     }
180 }