code: /*
   * Copyright (c) 2008-2019 Emmanuel Dupuy.
   * This project is distributed under the GPLv3 license.
   * This is a Copyleft license that gives the user the right to use,
   * copy and modify the code freely for non-commercial purposes.
   */
  
  package org.jd.gui.view.component;
  
  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
  import org.jd.gui.api.API;
  import org.jd.gui.api.feature.IndexesChangeListener;
  import org.jd.gui.api.feature.UriGettable;
  import org.jd.gui.api.model.Container;
  import org.jd.gui.api.model.Indexes;
  import org.jd.gui.util.exception.ExceptionUtil;
  import org.jd.gui.util.index.IndexesUtil;
  import org.jd.gui.util.io.TextReader;
  import org.jd.gui.util.xml.AbstractXmlPathFinder;
  
  import java.awt.*;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.util.*;
  import java.util.List;
  import java.util.concurrent.Future;
  
  public class WebXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {
      protected API api;
      protected Container.Entry entry;
      protected Collection<Future<Indexes>> collectionOfFutureIndexes;
  
      public WebXmlFilePage(API api, Container.Entry entry) {
          this.api = api;
          this.entry = entry;
          // Load content file
          String text = TextReader.getText(entry.getInputStream());
          // Create hyperlinks
          new PathFinder().find(text);
          // Display
          setText(text);
      }
  
      public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; }
  
      protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; }
  
      protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {
          TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData;
  
          if (data.enabled) {
              try {
                  // Save current position in history
                  Point location = textArea.getLocationOnScreen();
                  int offset = textArea.viewToModel(new Point(x-location.x, y-location.y));
                  URI uri = entry.getUri();
                  api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), "position=" + offset, null));
  
                  // Open link
                  if (hyperlinkData instanceof PathHyperlinkData) {
                      PathHyperlinkData d = (PathHyperlinkData)hyperlinkData;
                      String path = d.path;
                      Container.Entry entry = searchEntry(this.entry.getContainer().getRoot(), path);
                      if (entry != null) {
                          api.openURI(x, y, Collections.singletonList(entry), null, path);
                      }
                  } else {
                      String internalTypeName = data.internalTypeName;
                      List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName);
                      String rootUri = entry.getContainer().getRoot().getUri().toString();
                      ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();
  
                      for (Container.Entry entry : entries) {
                          if (entry.getUri().toString().startsWith(rootUri)) {
                              sameContainerEntries.add(entry);
                          }
                      }
  
                      if (sameContainerEntries.size() > 0) {
                          api.openURI(x, y, sameContainerEntries, null, data.internalTypeName);
                      } else if (entries.size() > 0) {
                          api.openURI(x, y, entries, null, data.internalTypeName);
                      }
                  }
              } catch (URISyntaxException e) {
                  assert ExceptionUtil.printStackTrace(e);
              }
          }
      }
  
      public static Container.Entry searchEntry(Container.Entry parent, String path) {
          if (path.charAt(0) == '/')
              path = path.substring(1);
          return recursiveSearchEntry(parent, path);
      }
  
      public static Container.Entry recursiveSearchEntry(Container.Entry parent, String path) {
          Container.Entry entry = null;
  
          for (Container.Entry child : parent.getChildren()) {
              if (path.equals(child.getPath())) {
                  entry = child;
                  break;
              }
          }
  
          if (entry != null) {
              return entry;
          } else {
              for (Container.Entry child : parent.getChildren()) {
                  if (path.startsWith(child.getPath() + '/')) {
                      entry = child;
                      break;
                  }
              }
  
              return (entry != null) ? searchEntry(entry, path) : null;
          }
      }
  
      // --- UriGettable --- //
      public URI getUri() { return entry.getUri(); }
  
      // --- ContentSavable --- //
      public String getFileName() {
          String path = entry.getPath();
          int index = path.lastIndexOf('/');
          return path.substring(index+1);
      }
  
      // --- IndexesChangeListener --- //
      public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {
          // Update the list of containers
          this.collectionOfFutureIndexes = collectionOfFutureIndexes;
          // Refresh links
          boolean refresh = false;
  
          for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {
              TypeHyperlinkData data = (TypeHyperlinkData)entry.getValue();
              boolean enabled;
  
              if (data instanceof PathHyperlinkData) {
                  PathHyperlinkData d = (PathHyperlinkData)data;
                  enabled = searchEntry(this.entry.getContainer().getRoot(), d.path) != null;
              } else {
                  String internalTypeName = data.internalTypeName;
                  enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName);
              }
  
              if (data.enabled != enabled) {
                  data.enabled = enabled;
                  refresh = true;
              }
          }
  
          if (refresh) {
              textArea.repaint();
          }
      }
  
      public static class PathHyperlinkData extends TypeHyperlinkData {
          public boolean enabled;
          public String path;
  
          PathHyperlinkData(int startPosition, int endPosition, String path) {
              super(startPosition, endPosition, null);
              this.enabled = false;
              this.path = path;
          }
      }
  
      protected static List<String> typeHyperlinkPaths = Arrays.asList(
          "web-app/filter/filter-class",
          "web-app/listener/listener-class",
          "web-app/servlet/servlet-class");
  
      protected static List<String> pathHyperlinkPaths = Arrays.asList(
          "web-app/jsp-config/taglib/taglib-location",
          "web-app/welcome-file-list/welcome-file",
          "web-app/login-config/form-login-config/form-login-page",
          "web-app/login-config/form-login-config/form-error-page",
          "web-app/jsp-config/jsp-property-group/include-prelude",
          "web-app/jsp-config/jsp-property-group/include-coda");
  
      protected static List<String> hyperlinkPaths = new ArrayList<>(typeHyperlinkPaths.size() + pathHyperlinkPaths.size());
  
      static {
          hyperlinkPaths.addAll(typeHyperlinkPaths);
          hyperlinkPaths.addAll(pathHyperlinkPaths);
      }
  
      public class PathFinder extends AbstractXmlPathFinder {
          public PathFinder() {
              super(hyperlinkPaths);
          }
  
          public void handle(String path, String text, int position) {
              String trim = text.trim();
              if (trim != null) {
                  int startIndex = position + text.indexOf(trim);
                  int endIndex = startIndex + trim.length();
  
                  if (pathHyperlinkPaths.contains(path)) {
                      addHyperlink(new PathHyperlinkData(startIndex, endIndex, trim));
                  } else {
                      String internalTypeName = trim.replace('.', '/');
                      addHyperlink(new TypeHyperlinkData(startIndex, endIndex, internalTypeName));
                  }
              }
          }
      }
  }
  