public XMLPageManifestLoader(String basePath, Collection<String> activeProfiles) { this.basePath = basePath; this.activeProfiles = activeProfiles; try { documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); SERVICE_LOADER.forEach(resourcesReaders::add); xPath = XPathFactory.newInstance().newXPath(); importsExpr = xPath.compile("/manifest/import"); viewsExpr = xPath.compile("/manifest/view"); resourcesExpr = xPath.compile("/manifest/resources"); fragmentsExpr = xPath.compile("/manifest/fragment"); selectorExpr = xPath.compile("/manifest/@selector"); } catch (ParserConfigurationException | XPathExpressionException e) { throw new JumbleException("Fail to initialize manifest loader", e); } }
/** Created by Hatis on 15.09.2015. */ public class XMLPageManifestLoader implements PageManifestLoader { private static final ServiceLoader<ResourceReader> SERVICE_LOADER = ServiceLoader.load(ResourceReader.class); private Collection<ResourceReader> resourcesReaders = new ArrayList<>(); private XPathExpression resourcesExpr; private XPathExpression viewsExpr; private XPathExpression importsExpr; private XPathExpression fragmentsExpr; private XPathExpression selectorExpr; private Collection<XPathExpression> fragmentRefExpressions = new ArrayList<>(); private Collection<XPathExpression> resourcesRefExpressions = new ArrayList<>(); private String basePath; private final Collection<String> activeProfiles; private Set<String> loadedFiles; private DocumentBuilder documentBuilder; private XPath xPath; public XMLPageManifestLoader(String basePath, Collection<String> activeProfiles) { this.basePath = basePath; this.activeProfiles = activeProfiles; try { documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); SERVICE_LOADER.forEach(resourcesReaders::add); xPath = XPathFactory.newInstance().newXPath(); importsExpr = xPath.compile("/manifest/import"); viewsExpr = xPath.compile("/manifest/view"); resourcesExpr = xPath.compile("/manifest/resources"); fragmentsExpr = xPath.compile("/manifest/fragment"); selectorExpr = xPath.compile("/manifest/@selector"); } catch (ParserConfigurationException | XPathExpressionException e) { throw new JumbleException("Fail to initialize manifest loader", e); } } public XMLPageManifestLoader(Collection<String> activeProfiles) { this("classpath:", activeProfiles); } public XMLPageManifestLoader() { this("classpath:", null); } public void addResourceReader(ResourceReader reader) { resourcesReaders.add(reader); } public void addResourceReaders(Collection<ResourceReader> readers) { resourcesReaders.addAll(readers); } // load @Override public synchronized PageManifest load(String manifestPath, String... alsoLoadProfiles) { try { loadedFiles = new HashSet<>(); createProfilesXPath(alsoLoadProfiles); PageManifestDef pmd = loadRootPageDefinition(manifestPath); return new PageManifestBuilder().buildManifest(pmd); } catch (ManifestNotFound e) { return null; } catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException e) { throw new PageManifestLoadingException("Fail to load page manifest: " + manifestPath, e); } } private void createProfilesXPath(String... alsoLoadProfiles) throws XPathExpressionException { fragmentRefExpressions.add(xPath.compile("fragment")); resourcesRefExpressions.add(xPath.compile("resources")); for (String profile : activeProfiles) { fragmentRefExpressions.add(xPath.compile("profile[@name='" + profile + "']/fragment")); resourcesRefExpressions.add(xPath.compile("profile[@name='" + profile + "']/resources")); } for (String profile : alsoLoadProfiles) { fragmentRefExpressions.add(xPath.compile("profile[@name='" + profile + "']/fragment")); resourcesRefExpressions.add(xPath.compile("profile[@name='" + profile + "']/resources")); } } private PageManifestDef loadRootPageDefinition(String manifestPathOrPath) throws ParserConfigurationException, SAXException, XPathExpressionException, IOException { PageManifestDef pmd = new PageManifestDef(); Document document = readPageDefinitionFile(manifestPathOrPath, pmd); pmd.setSelectorStrategy(selectorExpr.evaluate(document)); return pmd; } private Document readPageDefinitionFile(String manifestPathOrPath, PageManifestDef pmd) { FileScope fileScope = new FileScope(basePath, manifestPathOrPath); // prevent cycle import if (loadedFiles.contains(fileScope.getFullPath())) return null; String filePath = fileScope.getFullPath(); try { InputStream is = FileUtils.openStream(filePath); if (is == null) throw new JumbleException("Cant open file stream: " + fileScope.getFullPath()); Document document = documentBuilder.parse(is); loadedFiles.add(fileScope.getFullPath()); new PageManifestReader().read(fileScope, document, pmd); return document; } catch (FileNotFoundException e) { throw new ManifestNotFound("File not found: " + filePath); } catch (XPathExpressionException | IOException | SAXException e) { throw new PageManifestLoadingException("Cant read file: " + filePath, e); } } // read class PageManifestReader { private FileScope fileScope; public PageManifestDef read(FileScope fileScope, Document document, PageManifestDef pmd) throws XPathExpressionException { this.fileScope = fileScope; XmlUtils.iterateSubElements( document, resourcesExpr, e -> pmd.addResourceSet(readResourcesDefinition(e))); XmlUtils.iterateSubElements( document, fragmentsExpr, e -> pmd.addFragments(readFragmentDefinition(e))); XmlUtils.iterateSubElements(document, viewsExpr, e -> pmd.addView(readViewDefinition(e))); List<String> imports = XmlUtils.getSingleAttrNodes( document, importsExpr, "file", "import tag must contain file attribute"); imports .stream() .forEach( i -> XMLPageManifestLoader.this.readPageDefinitionFile( fileScope.getRelativePath(i), pmd)); return pmd; } private ViewDefinition readViewDefinition(Element element) { String name = element.getAttribute("name"); Assert.ifEmpty(name, "can`t read view without name"); String extend = element.getAttribute("extend"); String file = element.getAttribute("file"); if (isNoneEmpty(file)) file = fileScope.getRelativePath(file); ViewDefinition viewDef = new ViewDefinition(name, file); viewDef.setExtend(extend); fragmentRefExpressions.forEach( x -> { XmlUtils.iterateSubElements( element, x, e -> viewDef.addFragment(readFragmentReference(e))); }); resourcesRefExpressions.forEach( x -> { XmlUtils.iterateSubElements( element, x, e -> viewDef.addResources(readResourcesReference(e))); }); return viewDef; } private FragmentDef readFragmentDefinition(Element element) { String name = element.getAttribute("name"); Assert.ifEmpty(name, "can`t read resources bundle without name"); String file = element.getAttribute("file"); if (isNoneEmpty(file)) file = fileScope.getRelativePath(file); Assert.ifEmpty(file, "fragment definition must contain file attribute"); FragmentDef fragmentDef = new FragmentDef(name, file); readFragmentInnerContent(element, fragmentDef); return fragmentDef; } private FragmentReferenceDef readFragmentReference(Element element) { String name = element.getAttribute("name"); Assert.ifEmpty(name, "can`t read fragment without name"); String file = element.getAttribute("file"); if (isNoneEmpty(file)) file = fileScope.getRelativePath(file); FragmentReferenceDef fragmentDef = new FragmentReferenceDef(name, file); String layout = element.getAttribute("layout"); fragmentDef.setLayout(layout); readFragmentInnerContent(element, fragmentDef); return fragmentDef; } private void readFragmentInnerContent(Element element, FragmentDef fragmentDef) { fragmentRefExpressions.forEach( x -> { XmlUtils.iterateSubElements( element, x, e -> fragmentDef.addInnerFragment(readFragmentReference(e))); }); resourcesRefExpressions.forEach( x -> { XmlUtils.iterateSubElements( element, x, e -> fragmentDef.addResources(readResourcesReference(e))); }); } private ResourcesDef readResourcesReference(Element resourcesElement) { String name = resourcesElement.getAttribute("name"); ResourcesDef resDef = new ResourcesDef(name); readResources(resourcesElement, resDef); return resDef; } private ResourcesDef readResourcesDefinition(Element resourcesElement) { String name = resourcesElement.getAttribute("name"); Assert.ifEmpty(name, "can`t read resources bundle without name"); ResourcesDef resDef = new ResourcesDef(name); String depends = resourcesElement.getAttribute("depends"); if (isNoneEmpty(depends)) { resDef.setDepends( Arrays.stream(StringUtils.split(depends, ",")) .map(StringUtils::trim) .collect(Collectors.toList())); } readResources(resourcesElement, resDef); return resDef; } private void readResources(Element resourcesElement, ResourcesDef resDef) { XmlUtils.iterateSubElements( resourcesElement, element -> { XmlManifestResourceElement resourceElement = new XmlManifestResourceElement(element); Optional<ResourceReader> optional = resourcesReaders .stream() .filter(reader -> reader.accept(resourceElement)) .findFirst(); try { if (optional.isPresent()) resDef.addResource(optional.get().read(resourceElement, fileScope)); } catch (Exception e) { throw new JumbleException("Fail to load resource: " + element.getTagName(), e); } }); } } }