Java tutorial
/* * Copyright 2015 berni. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kisoonlineapp; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.apache.commons.lang.StringUtils; import org.junit.After; import org.junit.AfterClass; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * * @author berni */ public class XthmlCheckTest { private static final Logger logger = Logger.getLogger("XthmlCheckTest"); private final String srcmainwebapp = "src/main/webapp"; private final File defaultBaseDir = new File(srcmainwebapp); @BeforeClass public static void setUpClass() { } @AfterClass public static void tearDownClass() { } private DocumentBuilder documentBuilder; private XPath xPath; @Before public void setUp() throws ParserConfigurationException { final XMLAndXPathFactory f = new XMLAndXPathFactory(); this.documentBuilder = f.createDocumentBuilder(); this.xPath = f.createXPath(); } @After public void tearDown() { } /** * Assert that specific elements have id-attribute. * * @throws Exception */ @Test public void testCheckMissingIds() throws Exception { final List<String> messageList = new ArrayList<>(); final List<File> theFilesToCheck = gobbleAllFilesMatching(0, defaultBaseDir); assertTrue("#Files " + theFilesToCheck.size(), theFilesToCheck.size() > 0); final CheckMissingIds checkMissingIds = new CheckMissingIds(); for (File theFile : theFilesToCheck) { logger.log(Level.FINEST, "Checking file {0}", theFile); //--- final List<String> aMessageList = checkMissingIds.checkFile(theFile); messageList.addAll(aMessageList); } if (!messageList.isEmpty()) { fail(messageList.toString()); } } /** * Assert that elements have unique id-attribute. * * @throws Exception */ @Test public void testChekDuplicateIds() throws Exception { final List<String> messageList = new ArrayList<>(); final List<File> theFilesToCheck = gobbleAllFilesMatching(0, defaultBaseDir); assertTrue("#Files " + theFilesToCheck.size(), theFilesToCheck.size() > 0); final ChekDuplicateIds chekDuplicateIds = new ChekDuplicateIds(); for (File theFile : theFilesToCheck) { logger.log(Level.FINEST, "Checking file {0}", theFile); //--- List<String> aMessageList = chekDuplicateIds.checkFile(theFile); messageList.addAll(aMessageList); } if (!messageList.isEmpty()) { //logger.log(Level.FINEST, warningMessageList.toString()); fail(messageList.toString()); } } /** * Assert that specific elements have id-attribute. * * @throws Exception */ @Test public void testCheckMissingAttrs() throws Exception { final List<String> messageList = new ArrayList<>(); final List<File> theFilesToCheck = gobbleAllFilesMatching(0, defaultBaseDir); assertTrue("#Files " + theFilesToCheck.size(), theFilesToCheck.size() > 0); final List<ICheckFile> checkFileList = Arrays.asList(new CheckMissingRole()); for (ICheckFile icheckFile : checkFileList) { for (File theFile : theFilesToCheck) { logger.log(Level.FINEST, "Using {0} checking file {1}", new Object[] { icheckFile.getClass().getName(), theFile }); //--- List<String> aMessageList = icheckFile.checkFile(theFile); messageList.addAll(aMessageList); } if (!messageList.isEmpty()) { fail(messageList.toString()); } } } interface ICheckFile { List<String> checkFile(File theFile) throws Exception; } /** * Kapselung um fehlende id-Attributes zu finden */ private class CheckMissingRole implements ICheckFile { private final List<XPathExpression> xPathExpressionList; CheckMissingRole() throws XPathExpressionException { XPathCompilerFactory xPathCompilerFactory = new XPathCompilerFactory(); xPathExpressionList = xPathCompilerFactory.compileExpressions(xPath, new String[] { // "//h:commandButton", "//h:commandLink", "//h:outputLink", "//h:form", }); } @Override public List<String> checkFile(File theFile) throws Exception { final List<String> failMessageList = new ArrayList<>(); String attrName = "role"; List<NodeList> listOfMatchingNodeList = retrieveMatchingElementsFrom(theFile); for (NodeList matchingNodeList : listOfMatchingNodeList) { for (int i = 0; i < matchingNodeList.getLength(); i++) { Node aNode = matchingNodeList.item(i); logger.log(Level.FINEST, "Checking node {0}", aNode); NamedNodeMap aNamedNodeMap = aNode.getAttributes(); Node aIdAttributeNode = aNamedNodeMap.getNamedItem(attrName); if (aIdAttributeNode == null) { String msg = ">>> >>>" + "\n" + "Missing attribute " + attrName + " in " + theFile + ", " + formatNode(aNode) + "\n" + formatNamedNodeMap(aNamedNodeMap) + "<<< <<<" + "\n"; failMessageList.add(msg); } } } return failMessageList; } /** * Parse and match xml against some xpath-expressions. * * @param theFile * @return list of matching nodes list. * * @throws ParserConfigurationException * @throws XPathExpressionException * @throws FileNotFoundException * @throws SAXException * @throws IOException */ private List<NodeList> retrieveMatchingElementsFrom(File theFile) throws ParserConfigurationException, XPathExpressionException, FileNotFoundException, SAXException, IOException { final Document document = documentBuilder.parse(theFile); // Quercheck xmlns-prefix muss prefix in NamespaceContextFactory matchen final List<NodeList> listOfNodeList = new ArrayList<>(); for (int i = 0; i < xPathExpressionList.size(); i++) { XPathExpression xPathExpression = xPathExpressionList.get(i); NodeList nodeList = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET); logger.log(Level.FINEST, "Evaluated {0}: {1}, {2}", new Object[] { xPathExpression.toString() + ": " + nodeList + ", " + nodeList.getLength() }); listOfNodeList.add(nodeList); } return listOfNodeList; } } /** * Kapselung um fehlende id-Attributes zu finden */ private class CheckMissingIds implements ICheckFile { private final List<XPathExpression> xPathExpressionList; CheckMissingIds() throws XPathExpressionException { XPathCompilerFactory xPathCompilerFactory = new XPathCompilerFactory(); xPathExpressionList = xPathCompilerFactory.compileExpressions(xPath, new String[] { // "//h:inputTextarea", "//h:inputText", "//h:inputSecret", "//h:inputHidden", "//h:commandButton", "//h:commandLink", "//h:form", "//h:selectOneListbox", "//h:selectOneMenu", "//h:selectOneRadio", "//h:selectBooleanCheckbox", "//h:selectManyCheckbox", "//h:selectManyListbox", "//h:selectManyMenu", "//input", "//textarea", }); } @Override public List<String> checkFile(File theFile) throws Exception { final List<String> failMessageList = new ArrayList<>(); final Set<String> idValueSet = new HashSet<>(); List<NodeList> listOfMatchingNodeList = retrieveMatchingElementsFrom(theFile); for (NodeList matchingNodeList : listOfMatchingNodeList) { for (int i = 0; i < matchingNodeList.getLength(); i++) { Node aNode = matchingNodeList.item(i); logger.log(Level.FINEST, "Checking node {0}", aNode); NamedNodeMap aNamedNodeMap = aNode.getAttributes(); Node aIdAttributeNode = aNamedNodeMap.getNamedItem("id"); if (aIdAttributeNode == null) { String msg = ">>> >>>" + "\n" + "Missing attribute id in " + theFile + ", " + formatNode(aNode) + "\n" + formatNamedNodeMap(aNamedNodeMap) + "<<< <<<" + "\n"; failMessageList.add(msg); } else { String idValue = aIdAttributeNode.getNodeValue(); if (idValueSet.contains(idValue)) { String msg = ">>> >>>" + "\n" + "Duplicate attribute id value in " + theFile + ", " + formatNode(aNode) + "\n" + formatNamedNodeMap(aNamedNodeMap) + "<<< <<<" + "\n"; failMessageList.add(msg); } else { idValueSet.add(idValue); } } } } return failMessageList; } /** * Parse and match xml against some xpath-expressions. * * @param theFile * @return list of matching nodes list. * * @throws ParserConfigurationException * @throws XPathExpressionException * @throws FileNotFoundException * @throws SAXException * @throws IOException */ private List<NodeList> retrieveMatchingElementsFrom(File theFile) throws ParserConfigurationException, XPathExpressionException, FileNotFoundException, SAXException, IOException { final Document document = documentBuilder.parse(theFile); // Quercheck xmlns-prefix muss prefix in NamespaceContextFactory matchen final List<NodeList> listOfNodeList = new ArrayList<>(); for (int i = 0; i < xPathExpressionList.size(); i++) { XPathExpression xPathExpression = xPathExpressionList.get(i); NodeList nodeList = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET); logger.log(Level.FINEST, "Evaluated {0}: {1}, {2}", new Object[] { xPathExpression.toString(), nodeList, nodeList.getLength() }); listOfNodeList.add(nodeList); } return listOfNodeList; } } /** * Kapselung um duplicate-ids zu finden. * */ private class ChekDuplicateIds implements ICheckFile { private final List<XPathExpression> xPathExpressionList; ChekDuplicateIds() throws XPathExpressionException { XPathCompilerFactory xPathCompilerFactory = new XPathCompilerFactory(); xPathExpressionList = xPathCompilerFactory.compileExpressions(xPath, new String[] { "//@id", }); } @Override public List<String> checkFile(File theFile) throws Exception { final List<String> failMessageList = new ArrayList<>(); final Set<String> idValueSet = new HashSet<>(); List<NodeList> listOfMatchingNodeList = retrieveMatchingIdAttributeFrom(theFile); for (NodeList matchingNodeList : listOfMatchingNodeList) { for (int i = 0; i < matchingNodeList.getLength(); i++) { Node aNode = matchingNodeList.item(i); logger.log(Level.FINEST, "Checking node ", aNode); String idValue = aNode.getNodeValue(); if (idValueSet.contains(idValue)) { String msg = ">>> >>>" + "\n" + "Duplicate attribute id value in " + theFile + ", " + formatNode(aNode) + "\n" + "<<< <<<" + "\n"; failMessageList.add(msg); } else { idValueSet.add(idValue); } } } return failMessageList; } private List<NodeList> retrieveMatchingIdAttributeFrom(File theFile) throws ParserConfigurationException, XPathExpressionException, FileNotFoundException, SAXException, IOException { final Document document = documentBuilder.parse(theFile); // Quercheck xmlns-prefix muss prefix in NamespaceContextFactory matchen final List<NodeList> listOfNodeList = new ArrayList<>(); for (int i = 0; i < xPathExpressionList.size(); i++) { final XPathExpression xPathExpression = xPathExpressionList.get(i); NodeList nodeList = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET); logger.log(Level.FINEST, "Evaluated {0}: {1}, {2}", new Object[] { xPathExpression.toString(), nodeList, nodeList.getLength() }); listOfNodeList.add(nodeList); } return listOfNodeList; } } /** * Huebsches String-Format fuer einen Node * * @param n * @return human readable repr. of a Node */ private String formatNode(Node n) { final String SEP = "\n"; StringBuilder sb = new StringBuilder(); sb.append(n.getNodeName()).append(SEP).append(n.getNodeValue()).append(SEP).append(n.getNodeType()) .append(SEP).append(n.getBaseURI()).append(SEP).append(n.getLocalName()).append(SEP) .append(n.getNamespaceURI()).append(SEP).append(n.getPrefix()).append(SEP); String formatNode = sb.toString(); return formatNode; } /** * Huebsches String-Format fuer einen NamedNodeMap (ie. attributes) * * @param nnm * @return human readable repr. of a Node */ private String formatNamedNodeMap(NamedNodeMap nnm) { final String SEP = "\n"; StringBuilder sb = new StringBuilder(); sb.append(nnm.getLength()); for (int i = 0; i < nnm.getLength(); i++) { Node n = nnm.item(i); sb.append("#").append(i).append(n.getNodeName()).append(": ").append(n.getNodeValue()).append(SEP); } String formatNamedNodeMap = sb.toString(); return formatNamedNodeMap; } /** * Find all xhtml files recurivly * * @param depth * @param baseDir * @return */ private List<File> gobbleAllFilesMatching(int depth, File baseDir) { final int MAX_DEPTH = 50; final List<File> theFiles = new ArrayList<>(); if (depth > MAX_DEPTH) { return theFiles; } final XhtmlFileFilter xhtmlFileFilter = new XhtmlFileFilter(); File[] files = baseDir.listFiles(xhtmlFileFilter); if (files != null && files.length > 0) { for (File aFile : Arrays.asList(files)) { if (aFile.isDirectory()) { List<File> filesToAdd = gobbleAllFilesMatching(depth + 1, aFile); theFiles.addAll(filesToAdd); } else { theFiles.add(aFile); } } } return theFiles; } static class XMLAndXPathFactory { public XPath createXPath() { XPath xpath = XPathFactory.newInstance().newXPath(); final NamespaceContextFactory namespaceContextFactory = new NamespaceContextFactory(); xpath.setNamespaceContext(namespaceContextFactory.createNamespaceContext()); return xpath; } /** * Factory for setting up xpath namespace-context */ private static class NamespaceContextFactory { private final Map<String, String> mapPrefixNamespaceURI = new HashMap<>(); NamespaceContextFactory() { final String[] prefixNamespaceURI = { "", "http://www.w3.org/1999/xhtml", "xhtml", "http://www.w3.org/1999/xhtml", "ui", "http://java.sun.com/jsf/facelets", "f", "http://java.sun.com/jsf/core", "h", "http://java.sun.com/jsf/html", "kiso", "http://kisoonlineapp/facelets", "forge", "http://jboss.org/forge/view", }; for (int i = 0; i < prefixNamespaceURI.length; i += 2) { String prefix = prefixNamespaceURI[i]; String uri = prefixNamespaceURI[i + 1]; mapPrefixNamespaceURI.put(prefix, uri); } } NamespaceContext createNamespaceContext() { NamespaceContext namespaceContext = new NamespaceContext() { @Override public String getNamespaceURI(String prefix) { String uri = mapPrefixNamespaceURI.get(prefix); if (uri == null) { uri = XMLConstants.NULL_NS_URI; } return uri; } @Override public Iterator getPrefixes(String val) { return null; } @Override public String getPrefix(String uri) { return null; } }; return namespaceContext; } } /** * Create DocumentBuilder which is namespace-aware, having fast * entity-resolver, and throwing exceptions in case of parsing * exceptions. * * @return * @throws ParserConfigurationException */ public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); builderFactory.setNamespaceAware(true); builderFactory.setValidating(false); assertEquals(false, builderFactory.isCoalescing()); assertEquals(true, builderFactory.isExpandEntityReferences()); assertEquals(false, builderFactory.isIgnoringComments()); assertEquals(false, builderFactory.isIgnoringElementContentWhitespace()); assertEquals(true, builderFactory.isNamespaceAware()); assertEquals(false, builderFactory.isValidating()); assertEquals(false, builderFactory.isXIncludeAware()); final DocumentBuilder builder = builderFactory.newDocumentBuilder(); assertEquals(true, builder.isNamespaceAware()); assertEquals(false, builder.isValidating()); // Use dummy entity resolver, skipping reading the systemId // takes to much time parsing systemIds like "http://www.w3c.org/.... builder.setEntityResolver(createEntityResolver()); builder.setErrorHandler(createErrorHandler()); return builder; } protected EntityResolver createEntityResolver() { EntityResolver entityResolver = new EntityResolver() { @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { logger.log(Level.FINEST, "resolveEntity {0} {1}", new Object[] { publicId, systemId }); byte[] buf = new byte[0]; ByteArrayInputStream bais = new ByteArrayInputStream(buf); // return an empty input source.... InputSource inputSource = new InputSource(bais); //String minimalDTD = "<!--- minimal dtd -->"; //InputSource inputSource = new InputSource(new StringReader(minimalDTD)); return inputSource; } }; return entityResolver; } protected ErrorHandler createErrorHandler() { ErrorHandler errorHandler = new ErrorHandler() { @Override public void warning(SAXParseException exception) throws SAXException { throw new RuntimeException("Parsing warning ", exception); } @Override public void error(SAXParseException exception) throws SAXException { throw new RuntimeException("Parsing error ", exception); } @Override public void fatalError(SAXParseException exception) throws SAXException { throw new RuntimeException("Parsing fatalError ", exception); } }; return errorHandler; } } static class XPathCompilerFactory { List<XPathExpression> compileExpressions(XPath xPath, String[] expressions) throws XPathExpressionException { final List<XPathExpression> xPathExpressionList = new ArrayList<>(); for (int i = 0; i < expressions.length; i++) { final String expression = expressions[i]; final XPathExpression xPathExpression = xPath.compile(expression); xPathExpressionList.add(xPathExpression); } return xPathExpressionList; } } /** * File filter accepting only directory and files having ext ".xhtml" */ private static class XhtmlFileFilter implements FileFilter { @Override public boolean accept(File pathname) { final boolean acceptDir; acceptDir = pathname.isDirectory() // excluding dir names containing && !StringUtils.contains(pathname.getAbsolutePath(), ".svn") && !StringUtils.contains(pathname.getAbsolutePath(), ".git") && !StringUtils.contains(pathname.getAbsolutePath(), "tmp") && !StringUtils.contains(pathname.getAbsolutePath(), "temp"); final boolean acceptFile; acceptFile = pathname.canRead() && pathname.isFile() // accepting filename && StringUtils.endsWith(pathname.getName(), "xhtml"); final boolean accept = acceptDir || acceptFile; logger.log(Level.FINEST, "Filtering {0}: {1}", new Object[] { pathname, accept }); return accept; } } }