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  
20  package org.apache.myfaces.tobago.example.demo;
21  
22  import org.apache.commons.io.IOUtils;
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  
26  import javax.annotation.PostConstruct;
27  import javax.enterprise.context.ApplicationScoped;
28  import javax.enterprise.event.Event;
29  import javax.faces.context.ExternalContext;
30  import javax.faces.context.FacesContext;
31  import javax.inject.Inject;
32  import javax.inject.Named;
33  import javax.servlet.ServletContext;
34  import java.io.File;
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.io.Serializable;
38  import java.lang.invoke.MethodHandles;
39  import java.nio.charset.StandardCharsets;
40  import java.util.ArrayList;
41  import java.util.Collections;
42  import java.util.Enumeration;
43  import java.util.HashMap;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Set;
47  import java.util.regex.Pattern;
48  import java.util.zip.ZipEntry;
49  import java.util.zip.ZipException;
50  import java.util.zip.ZipFile;
51  
52  @ApplicationScoped
53  @Named
54  public class NavigationTree implements Serializable {
55  
56    private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
57  
58    private NavigationNode root;
59  
60    @Inject
61    private Event<NavigationNode> events;
62  
63    /**
64     * todo: Seems not working with Java EE 6, needs Java EE 7?
65     */
66  //  @Inject
67  //  private ServletContext servletContext;
68    public NavigationTree() {
69      LOG.info("<init> " + this);
70    }
71  
72    @PostConstruct
73    protected void postConstruct() {
74      // todo: refactor with Java EE 7
75      final ServletContext servletContext;
76      servletContext = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
77  
78      final List<NavigationNode> nodes = new ArrayList<>();
79  
80      final List<String> listWar = locateResourcesInWar(servletContext, "/content", new ArrayList<>());
81      addToResult(listWar, nodes);
82  
83  
84      final List<String> listClasspath = getResourcesFromClasspath();
85      addToResult(listClasspath, nodes);
86  
87      Collections.sort(nodes);
88  
89      // after sorting the first node is the root node.
90      root = nodes.get(0);
91  
92      final Map<String, NavigationNode> map = new HashMap<>();
93  
94      for (final NavigationNode node : nodes) {
95  //      LOG.debug("Creating node='{}' branch='{}'", node.getName(), node.getBranch());
96        map.put(node.getBranch(), node);
97        final String parent = node.getBranch().substring(0, node.getBranch().lastIndexOf('/'));
98        if (!parent.equals("")) { // is root
99          map.get(parent).add(node);
100       }
101       node.evaluateTreePath();
102     }
103   }
104 
105   private void addToResult(List<String> listWar, List<NavigationNode> nodes) {
106     for (final String path : listWar) {
107       if (path.contains("/x-") || !path.contains(".xhtml")) {
108         // ignoring excluded files
109         continue;
110       }
111       nodes.add(new NavigationNode(path, this));
112     }
113   }
114 
115   protected List<String> locateResourcesInWar(
116       final ServletContext servletContext, final String directory, final List<String> result) {
117 
118     final Set<String> resourcePaths = servletContext.getResourcePaths(directory);
119 
120     if (resourcePaths != null) {
121       for (final String resourcePath : resourcePaths) {
122 
123         final String path = resourcePath.substring(resourcePath.indexOf("/content")); // Quarkus may have full path
124 
125         if (path.endsWith("/")  // is directory
126             || path.lastIndexOf('.') < path.lastIndexOf('/')) { // quarkus has no '/' at the end of a dir.
127           locateResourcesInWar(servletContext, path.substring(path.indexOf("/content")), result);
128           continue;
129         }
130 
131         result.add(path);
132       }
133     }
134     return result;
135   }
136 
137   public NavigationNode findByViewId(final String viewId) {
138     if (viewId != null) {
139       final Enumeration enumeration = root.depthFirstEnumeration();
140       while (enumeration.hasMoreElements()) {
141         final NavigationNode node = (NavigationNode) enumeration.nextElement();
142         if (node.getOutcome() != null && viewId.contains(node.getOutcome())) {
143           return node;
144         }
145       }
146     }
147     return null;
148   }
149 
150   public NavigationNode getTree() {
151     return root;
152   }
153 
154   public void gotoNode(final NavigationNode node) {
155     if (node != null) {
156       events.fire(node);
157     } else {
158       events.fire(root);
159     }
160   }
161 
162   public String getSource() {
163     final FacesContext facesContext = FacesContext.getCurrentInstance();
164     final ExternalContext externalContext = facesContext.getExternalContext();
165     final String viewId = facesContext.getViewRoot().getViewId();
166     try (final InputStream resourceAsStream = externalContext.getResourceAsStream(viewId)) {
167       return IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
168     } catch (final IOException e) {
169       LOG.error("", e);
170       return "error";
171     }
172   }
173 
174   private List<String> getResourcesFromClasspath() {
175     Pattern pattern = Pattern.compile(".*/content/.*\\.xhtml");
176     final ArrayList<String> result = new ArrayList<>();
177     final String classPath = System.getProperty("java.class.path", ".");
178     final String[] classPathElements = classPath.split(System.getProperty("path.separator"));
179     for (final String element : classPathElements) {
180       result.addAll(getResourcesFromClasspath(element, pattern, "/content/"));
181     }
182     return result;
183   }
184 
185   private List<String> getResourcesFromClasspath(final String element, final Pattern pattern, final String base) {
186     final ArrayList<String> result = new ArrayList<>();
187     final File file = new File(element);
188     if (file.isDirectory()) {
189       result.addAll(getResourcesFromDirectory(file, pattern, base));
190     } else {
191       result.addAll(getResourcesFromJarFile(file, pattern, base));
192     }
193     return result;
194   }
195 
196   private List<String> getResourcesFromJarFile(final File file, final Pattern pattern, final String base) {
197     final ArrayList<String> result = new ArrayList<>();
198     ZipFile zip;
199     try {
200       zip = new ZipFile(file);
201     } catch (final ZipException e) {
202       throw new Error(e);
203     } catch (final IOException e) {
204       throw new Error(e);
205     }
206     final Enumeration e = zip.entries();
207     while (e.hasMoreElements()) {
208       final ZipEntry ze = (ZipEntry) e.nextElement();
209       final String fileName = ze.getName();
210       final boolean accept = pattern.matcher(fileName).matches();
211       if (accept) {
212         result.add(fileName.substring(fileName.indexOf(base)));
213       }
214     }
215     try {
216       zip.close();
217     } catch (final IOException e1) {
218       throw new Error(e1);
219     }
220     return result;
221   }
222 
223   private List<String> getResourcesFromDirectory(final File directory, final Pattern pattern, final String base) {
224     final ArrayList<String> result = new ArrayList<String>();
225     final File[] fileList = directory.listFiles();
226     for (final File file : fileList) {
227       if (file.isDirectory()) {
228         result.addAll(getResourcesFromDirectory(file, pattern, base));
229       } else {
230         try {
231           final String fileName = file.getCanonicalPath();
232           final boolean accept = pattern.matcher(fileName).matches();
233           if (accept) {
234             result.add(fileName.substring(fileName.indexOf(base)));
235           }
236         } catch (final IOException e) {
237           throw new Error(e);
238         }
239       }
240     }
241     return result;
242   }
243 }