/* * Copyright 2015 ArcBees Inc. * * 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 com.gwtplatform.processors.tools; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.stream.Collectors; import javax.lang.model.element.Element; import javax.lang.model.element.PackageElement; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.gwtplatform.processors.tools.logger.Logger; import com.gwtplatform.processors.tools.utils.Utils; public class GwtSourceFilter { public static final String GWTP_MODULE_OPTION = "gwtp.module"; private static final String MODULE_EXTENSION = ".gwt.xml"; private static final String SOURCE_TAG = "source"; private static final String PATH_ATTRIBUTE = "path"; private static final String INHERITS_TAG = "inherits"; private static final String NAME_ATTRIBUTE = "name"; private static final DocumentBuilder documentBuilder = createDocumentBuilder(); private final Logger logger; private final Utils utils; private final String[] moduleNames; private final Set<String> parsedModules; private final Set<String> sourcePackages; private boolean initialized; public GwtSourceFilter( Logger logger, Utils utils, String... moduleNames) { this.logger = logger; this.utils = utils; this.moduleNames = moduleNames; this.parsedModules = new HashSet<>(); this.sourcePackages = new LinkedHashSet<>(); } private static DocumentBuilder createDocumentBuilder() { try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); documentBuilder.setEntityResolver((publicId, systemId) -> { // Disable the loading of DTDs as it is a huge slow down during compilation. return new InputSource(new StringReader("")); }); return documentBuilder; } catch (ParserConfigurationException e) { throw new RuntimeException(e); } } public String getApplicationPackage() { return getSourcePackages().iterator().next(); } public Set<String> getSourcePackages() { if (!initialized) { resolveSourcePackages(); initialized = true; } return new LinkedHashSet<>(sourcePackages); } private void resolveSourcePackages() { if (moduleNames != null) { addModules(moduleNames); } loadFromOptions(); if (sourcePackages.isEmpty()) { logger.warning("No source packages found. Make sure your GWT module contains <source> tags."); } } private void loadFromOptions() { String modules = utils.getOption(GWTP_MODULE_OPTION); if (modules == null) { logger.warning("Add `-A%s=com.domain.YourModule` to your compiler options to enable GWTP " + "annotation processors.", GWTP_MODULE_OPTION); } else { addModules(modules.split(",")); } } public <E extends Element> Set<E> filterElements(Set<E> elements) { Set<E> filteredElements = elements.stream() .filter(element -> elementIsPartOfGwtSource(element)) .collect(Collectors.toSet()); return filteredElements; } public boolean elementIsPartOfGwtSource(Element element) { PackageElement packageElement = utils.getElements().getPackageOf(element); String packageName = packageElement.getQualifiedName() + "."; for (String sourcePackage : getSourcePackages()) { if (packageName.startsWith(sourcePackage + ".")) { return true; } } return false; } public void addModules(String[] moduleNames) { addModules(Arrays.asList(moduleNames)); } public void addModules(Collection<String> moduleNames) { for (String moduleName : moduleNames) { addModule(moduleName); } } public void addModule(String moduleName) { try { loadModule(moduleName); } catch (IOException | SAXException e) { logger.mandatoryWarning().throwable(e).log("Unable to parse module for source paths '%s'.", moduleName); } } private void loadModule(String moduleName) throws IOException, SAXException { if (moduleName == null || moduleName.isEmpty() || parsedModules.contains(moduleName)) { return; } parsedModules.add(moduleName); String moduleFileName = "/" + moduleName.replace('.', '/') + MODULE_EXTENSION; String moduleBasePackage = moduleFileName.substring(1, moduleFileName.lastIndexOf('/')).replace('/', '.'); try (InputStream moduleFile = GwtSourceFilter.class.getResourceAsStream(moduleFileName)) { if (moduleFile != null) { loadModule(moduleBasePackage, moduleFile); } else { logger.warning("Unable to find module file '%s' in source tree.", moduleName); } } } private void loadModule(String moduleBasePackage, InputStream moduleFile) throws IOException, SAXException { Document document = parseModule(moduleFile); loadSourcePackages(moduleBasePackage, document); loadInheritedModules(document); } private Document parseModule(InputStream moduleFile) throws SAXException, IOException { Document document = documentBuilder.parse(moduleFile); document.getDocumentElement().normalize(); return document; } private void loadSourcePackages(String moduleBasePackage, Document document) { NodeList sourceNodes = document.getElementsByTagName(SOURCE_TAG); for (int i = 0; i < sourceNodes.getLength(); ++i) { loadSourcePackage(moduleBasePackage, sourceNodes.item(i)); } } private void loadSourcePackage(String moduleBasePackage, Node sourceNode) { NamedNodeMap sourceAttributes = sourceNode.getAttributes(); Node pathAttribute = sourceAttributes.getNamedItem(PATH_ATTRIBUTE); String path = pathAttribute.getNodeValue(); String sourcePackage = moduleBasePackage; if (!path.isEmpty()) { sourcePackage += "." + path.replace('/', '.'); } sourcePackages.add(sourcePackage); } private void loadInheritedModules(Document document) throws IOException, SAXException { NodeList inheritsNodes = document.getElementsByTagName(INHERITS_TAG); for (int i = 0; i < inheritsNodes.getLength(); ++i) { loadInheritedModule(inheritsNodes.item(i)); } } private void loadInheritedModule(Node inheritsNode) throws IOException, SAXException { NamedNodeMap inheritsAttributes = inheritsNode.getAttributes(); Node nameAttribute = inheritsAttributes.getNamedItem(NAME_ATTRIBUTE); String name = nameAttribute.getNodeValue(); loadModule(name); } }