| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.myfaces.tobago.example.demo; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import jakarta.annotation.PostConstruct; |
| import jakarta.enterprise.context.ApplicationScoped; |
| import jakarta.enterprise.event.Event; |
| import jakarta.faces.context.ExternalContext; |
| import jakarta.faces.context.FacesContext; |
| import jakarta.inject.Inject; |
| import jakarta.inject.Named; |
| import jakarta.servlet.ServletContext; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Serializable; |
| import java.lang.invoke.MethodHandles; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| |
| @ApplicationScoped |
| @Named |
| public class NavigationTree implements Serializable { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
| |
| private NavigationNode root; |
| |
| @Inject |
| private Event<NavigationNode> events; |
| |
| @Inject |
| private ServletContext servletContext; |
| |
| public NavigationTree() { |
| LOG.info("<init> " + this); |
| } |
| |
| @PostConstruct |
| protected void postConstruct() { |
| |
| final List<NavigationNode> nodes = new ArrayList<>(); |
| |
| final List<String> listWar = locateResourcesInWar(servletContext, "/content", new ArrayList<>()); |
| addToResult(listWar, nodes); |
| |
| |
| final List<String> listClasspath = getResourcesFromClasspath(); |
| addToResult(listClasspath, nodes); |
| |
| Collections.sort(nodes); |
| |
| // after sorting the first node is the root node. |
| root = nodes.get(0); |
| |
| final Map<String, NavigationNode> map = new HashMap<>(); |
| |
| for (final NavigationNode node : nodes) { |
| // LOG.debug("Creating node='{}' branch='{}'", node.getName(), node.getBranch()); |
| map.put(node.getBranch(), node); |
| final String parent = node.getBranch().substring(0, node.getBranch().lastIndexOf('/')); |
| if (!parent.equals("")) { // is root |
| map.get(parent).add(node); |
| } |
| node.evaluateTreePath(); |
| } |
| } |
| |
| private void addToResult(List<String> listWar, List<NavigationNode> nodes) { |
| for (final String path : listWar) { |
| if (path.contains("/x-") || !path.contains(".xhtml")) { |
| // ignoring excluded files |
| continue; |
| } |
| nodes.add(new NavigationNode(path, this)); |
| } |
| } |
| |
| protected List<String> locateResourcesInWar( |
| final ServletContext servletContext, final String directory, final List<String> result) { |
| |
| final Set<String> resourcePaths = servletContext.getResourcePaths(directory); |
| |
| if (resourcePaths != null) { |
| for (final String resourcePath : resourcePaths) { |
| |
| final String path = resourcePath.substring(resourcePath.indexOf("/content")); // Quarkus may have full path |
| |
| if (path.endsWith("/") // is directory |
| || path.lastIndexOf('.') < path.lastIndexOf('/')) { // quarkus has no '/' at the end of a dir. |
| locateResourcesInWar(servletContext, path.substring(path.indexOf("/content")), result); |
| continue; |
| } |
| |
| result.add(path); |
| } |
| } |
| return result; |
| } |
| |
| public NavigationNode findByViewId(final String viewId) { |
| if (viewId != null) { |
| final Enumeration enumeration = root.depthFirstEnumeration(); |
| while (enumeration.hasMoreElements()) { |
| final NavigationNode node = (NavigationNode) enumeration.nextElement(); |
| if (node.getOutcome() != null && viewId.contains(node.getOutcome())) { |
| return node; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public NavigationNode getTree() { |
| return root; |
| } |
| |
| public void gotoNode(final NavigationNode node) { |
| if (node != null) { |
| events.fire(node); |
| } else { |
| events.fire(root); |
| } |
| } |
| |
| public String getSource() { |
| final FacesContext facesContext = FacesContext.getCurrentInstance(); |
| final ExternalContext externalContext = facesContext.getExternalContext(); |
| final String viewId = facesContext.getViewRoot().getViewId(); |
| try (InputStream resourceAsStream = externalContext.getResourceAsStream(viewId)) { |
| return IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8); |
| } catch (final IOException e) { |
| LOG.error("", e); |
| return "error"; |
| } |
| } |
| |
| private List<String> getResourcesFromClasspath() { |
| Pattern pattern = Pattern.compile(".*/content/.*\\.xhtml"); |
| final ArrayList<String> result = new ArrayList<>(); |
| final String classPath = System.getProperty("java.class.path", "."); |
| final String[] classPathElements = classPath.split(System.getProperty("path.separator")); |
| for (final String element : classPathElements) { |
| result.addAll(getResourcesFromClasspath(element, pattern, "/content/")); |
| } |
| return result; |
| } |
| |
| private List<String> getResourcesFromClasspath(final String element, final Pattern pattern, final String base) { |
| final ArrayList<String> result = new ArrayList<>(); |
| final File file = new File(element); |
| if (file.isDirectory()) { |
| result.addAll(getResourcesFromDirectory(file, pattern, base)); |
| } else { |
| result.addAll(getResourcesFromJarFile(file, pattern, base)); |
| } |
| return result; |
| } |
| |
| private List<String> getResourcesFromJarFile(final File file, final Pattern pattern, final String base) { |
| final ArrayList<String> result = new ArrayList<>(); |
| ZipFile zip; |
| try { |
| zip = new ZipFile(file); |
| } catch (final IOException e) { |
| throw new Error(e); |
| } |
| final Enumeration e = zip.entries(); |
| while (e.hasMoreElements()) { |
| final ZipEntry ze = (ZipEntry) e.nextElement(); |
| final String fileName = ze.getName(); |
| final boolean accept = pattern.matcher(fileName).matches(); |
| if (accept) { |
| result.add(fileName.substring(fileName.indexOf(base))); |
| } |
| } |
| try { |
| zip.close(); |
| } catch (final IOException e1) { |
| throw new Error(e1); |
| } |
| return result; |
| } |
| |
| private List<String> getResourcesFromDirectory(final File directory, final Pattern pattern, final String base) { |
| final ArrayList<String> result = new ArrayList<String>(); |
| final File[] fileList = directory.listFiles(); |
| for (final File file : fileList) { |
| if (file.isDirectory()) { |
| result.addAll(getResourcesFromDirectory(file, pattern, base)); |
| } else { |
| try { |
| final String fileName = file.getCanonicalPath(); |
| final boolean accept = pattern.matcher(fileName).matches(); |
| if (accept) { |
| result.add(fileName.substring(fileName.indexOf(base))); |
| } |
| } catch (final IOException e) { |
| throw new Error(e); |
| } |
| } |
| } |
| return result; |
| } |
| } |