Example #1
0
 public static void useSunInternalXPathSupport() throws Exception {
   Class.forName("com.sun.org.apache.xpath.internal.XPath");
   Class c = Class.forName("freemarker.ext.dom.SunInternalXalanXPathSupport");
   if (logger.isDebugEnabled()) {
     logger.debug("Using Sun's internal Xalan classes for XPath support");
   }
   xpathSupportClass = c;
 }
Example #2
0
 /**
  * Convenience method. Tells the system to use Xalan for XPath queries.
  *
  * @throws Exception if the Xalan XPath classes are not present.
  */
 public static void useXalanXPathSupport() throws Exception {
   Class.forName("org.apache.xpath.XPath");
   Class c = Class.forName("freemarker.ext.dom.XalanXPathSupport");
   if (logger.isDebugEnabled()) {
     logger.debug("Using Xalan classes for XPath support");
   }
   xpathSupportClass = c;
 }
Example #3
0
 /**
  * Convenience method. Tells the system to use Jaxen for XPath queries.
  *
  * @throws Exception if the Jaxen classes are not present.
  */
 public static void useJaxenXPathSupport() throws Exception {
   Class.forName("org.jaxen.dom.DOMXPath");
   Class c = Class.forName("freemarker.ext.dom.JaxenXPathSupport");
   jaxenXPathSupport = (XPathSupport) c.newInstance();
   if (logger.isDebugEnabled()) {
     logger.debug("Using Jaxen classes for XPath support");
   }
   xpathSupportClass = c;
 }
Example #4
0
 static {
   try {
     useDefaultXPathSupport();
   } catch (Exception e) {
     // do nothing
   }
   if (xpathSupportClass == null && logger.isWarnEnabled()) {
     logger.warn("No XPath support is available.");
   }
 }
Example #5
0
 @Override
 public void render(Environment env, Map params, TemplateDirectiveBody body)
     throws IOException, TemplateException {
   if (getSubject() == null || !getSubject().isAuthenticated()) {
     log.debug("Subject does not exist or is not authenticated.  Tag body will be evaluated.");
     renderBody(env, body);
   } else {
     log.debug("Subject exists and is authenticated.  Tag body will not be evaluated.");
   }
 }
Example #6
0
 @Override
 public void render(Environment env, Map params, TemplateDirectiveBody body)
     throws IOException, TemplateException {
   if (getSubject() != null && getSubject().getPrincipal() != null) {
     log.debug("Subject has known identity (aka 'principal'). Tag body will be evaluated.");
     renderBody(env, body);
   } else {
     log.debug(
         "Subject does not exist or have a known identity (aka 'principal'). Tag body will not be evaluated.");
   }
 }
  public Object findTemplateSource(String name) throws IOException {
    String fullPath = subdirPath + name;

    if (attemptFileAccess) {
      // First try to open as plain file (to bypass servlet container resource caches).
      try {
        String realPath = servletContext.getRealPath(fullPath);
        if (realPath != null) {
          File file = new File(realPath);
          if (file.canRead() && file.isFile()) {
            return file;
          }
        }
      } catch (SecurityException e) {; // ignore
      }
    }

    // If it fails, try to open it with servletContext.getResource.
    URL url = null;
    try {
      url = servletContext.getResource(fullPath);
    } catch (MalformedURLException e) {
      LOG.warn("Could not retrieve resource " + StringUtil.jQuoteNoXSS(fullPath), e);
      return null;
    }
    return url == null ? null : new URLTemplateSource(url, getURLConnectionUsesCaches());
  }
Example #8
0
 XPathSupport getXPathSupport() {
   if (jaxenXPathSupport != null) {
     return jaxenXPathSupport;
   }
   XPathSupport xps = null;
   Document doc = node.getOwnerDocument();
   if (doc == null) {
     doc = (Document) node;
   }
   synchronized (doc) {
     WeakReference ref = (WeakReference) xpathSupportMap.get(doc);
     if (ref != null) {
       xps = (XPathSupport) ref.get();
     }
     if (xps == null) {
       try {
         xps = (XPathSupport) xpathSupportClass.newInstance();
         xpathSupportMap.put(doc, new WeakReference(xps));
       } catch (Exception e) {
         logger.error("Error instantiating xpathSupport class", e);
       }
     }
   }
   return xps;
 }
Example #9
0
  @SuppressWarnings("rawtypes")
  @Override
  public void render(Environment env, Map params, TemplateDirectiveBody body)
      throws IOException, TemplateException {
    if (getSubject() != null && getSubject().isAuthenticated()) {
      if (log.isDebugEnabled()) {
        log.debug("Subject exists and is authenticated.  Tag body will be evaluated.");
      }

      renderBody(env, body);
    } else {
      if (log.isDebugEnabled()) {
        log.debug(
            "Subject does not exist or is not authenticated.  Tag body will not be evaluated.");
      }
    }
  }
 private static ExpressionFactory tryExpressionFactoryImplementation(String packagePrefix) {
   String className = packagePrefix + ".el.ExpressionFactoryImpl";
   try {
     Class cl = ClassUtil.forName(className);
     if (ExpressionFactory.class.isAssignableFrom(cl)) {
       logger.info(
           "Using " + className + " as implementation of " + ExpressionFactory.class.getName());
       return (ExpressionFactory) cl.newInstance();
     }
     logger.warn(
         "Class " + className + " does not implement " + ExpressionFactory.class.getName());
   } catch (ClassNotFoundException e) {
   } catch (Exception e) {
     logger.error("Failed to instantiate " + className, e);
   }
   return null;
 }
 private static ExpressionFactory findExpressionFactoryImplementation() {
   ExpressionFactory ef = tryExpressionFactoryImplementation("com.sun");
   if (ef == null) {
     ef = tryExpressionFactoryImplementation("org.apache");
     if (ef == null) {
       logger.warn("Could not find any implementation for " + ExpressionFactory.class.getName());
     }
   }
   return ef;
 }
Example #12
0
/**
 * Freemarker tag that renders the tag body only if the current user has <em>not</em> executed a
 * successful authentication attempt <em>during their current session</em>.
 *
 * <p>The logically opposite tag of this one is the {@link
 * org.apache.shiro.web.tags.AuthenticatedTag}.
 *
 * <p>Equivalent to {@link org.apache.shiro.web.tags.NotAuthenticatedTag}
 */
public class NotAuthenticatedTag extends SecureTag {
  static final Logger log = Logger.getLogger("NotAuthenticatedTag");

  @Override
  public void render(Environment env, Map params, TemplateDirectiveBody body)
      throws IOException, TemplateException {
    if (getSubject() == null || !getSubject().isAuthenticated()) {
      log.debug("Subject does not exist or is not authenticated.  Tag body will be evaluated.");
      renderBody(env, body);
    } else {
      log.debug("Subject exists and is authenticated.  Tag body will not be evaluated.");
    }
  }
}
Example #13
0
  @SuppressWarnings("unchecked")
  Object getPrincipalFromClassName(Map params) {
    String type = this.getType(params);

    try {
      Class cls = Class.forName(type);

      return getSubject().getPrincipals().oneByType(cls);
    } catch (ClassNotFoundException ex) {
      log.error("Unable to find class for name [" + type + "]", ex);
    }

    return null;
  }
  public static void main(String[] args) throws Exception {
    System.out.println(repeat("-", 80));
    for (String line : readLines(getResource("banner.txt"), Charsets.UTF_8)) {
      System.out.println(line);
    }
    String version = ComputeCommandLine.class.getPackage().getImplementationVersion();
    System.out.println("Version: " + (version != null ? version : "DEVELOPMENT"));
    System.out.println(repeat("-", 80));

    // disable freemarker logging
    freemarker.log.Logger.selectLoggerLibrary(freemarker.log.Logger.LIBRARY_NONE);

    // parse options
    ComputeProperties computeProperties = new ComputeProperties(args);

    new ComputeCommandLine().execute(computeProperties);
  }
  @SuppressWarnings("deprecation")
  private static synchronized void init() throws Exception {
    Version version = new Version(2, 3, 23);
    Logger.selectLoggerLibrary(0);
    cfg = new Configuration(version);
    //    cfg.setLogTemplateExceptions(true);
    cfg.setCacheStorage(new MruCacheStorage(0, Integer.MAX_VALUE));
    cfg.setObjectWrapper(new DefaultObjectWrapper(version));
    //      cfg.setDirectoryForTemplateLoading(new
    // File(FreemarkerUtil.class.getClassLoader().getResource("template").getFile() ));
    //      cfg.setClassForTemplateLoading(FreemarkerUtil.class, "template");
    StringTemplateLoader st = new StringTemplateLoader();
    //      st.putTemplate("autoMall",
    // "<AutoMall><docid>${autoMall.docid!}</docid><Id>${autoMall.id!}</Id><Name><![CDATA[${autoMall.name!}]]></Name><NameAlias><![CDATA[${autoMall.nameAlias!}]]></NameAlias><ImgUrl><![CDATA[${autoMall.imgUrl!}]]></ImgUrl><CarStyleId>${autoMall.carStyleId!}</CarStyleId><CategoryId>${autoMall.categoryId!}</CategoryId><CategoryLevel>${autoMall.categoryLevel!}</CategoryLevel><BrandId>${autoMall.brandId!}</BrandId><SpecificationsId>${autoMall.specificationsId!}</SpecificationsId><FactoryNumber>${autoMall.factoryNumber!}</FactoryNumber><ShelvesTime>${autoMall.shelvesTime!}</ShelvesTime><RegionId>${autoMall.regionId!}</RegionId><IsHstock>${autoMall.isHstock!}</IsHstock><IsCerGoods>${autoMall.isCerGoods!}</IsCerGoods><IsStarProducts>${autoMall.isStarProducts!}</IsStarProducts><SalesVolume>${autoMall.salesVolume!}</SalesVolume><Evaluation>${autoMall.evaluation!}</Evaluation><Price>${autoMall.price!}</Price><GoodsModel><![CDATA[${autoMall.goodsModel!}]]></GoodsModel><UserDefinedBrandName><![CDATA[${autoMall.userDefinedBrandName!}]]></UserDefinedBrandName><FactoryName><![CDATA[${autoMall.factoryName!}]]></FactoryName><CarStyleName><![CDATA[${autoMall.carStyleName!}]]></CarStyleName><ServiceStationName><![CDATA[${autoMall.serviceStationName!}]]></ServiceStationName><StoreId>${autoMall.storeId!}</StoreId><StoreName><![CDATA[${autoMall.storeName!}]]></StoreName><AttrValue><![CDATA[${autoMall.attrValue!}]]></AttrValue><CarName><![CDATA[${autoMall.carName!}]]></CarName><BrandName><![CDATA[${autoMall.brandName!}]]></BrandName></AutoMall>");

    String[] templates = new String[] {"carParts", "stores", "storesrecomm"};
    for (String template : templates) {
      st.putTemplate(template, CommonUtil.readFileInJar("/template/" + template));
    }
    cfg.setTemplateLoader(st);
  }
Example #16
0
/**
 * Tag used to print out the String value of a user's default principal, or a specific principal as
 * specified by the tag's attributes.
 *
 * <p>If no attributes are specified, the tag prints out the <tt>toString()</tt> value of the user's
 * default principal. If the <tt>type</tt> attribute is specified, the tag looks for a principal
 * with the given type. If the <tt>property</tt> attribute is specified, the tag prints the string
 * value of the specified property of the principal. If no principal is found or the user is not
 * authenticated, the tag displays nothing unless a <tt>defaultValue</tt> is specified.
 *
 * <p>Equivalent to {@link org.apache.shiro.web.tags.PrincipalTag}
 *
 * @since 0.2
 */
public class PrincipalTag extends SecureTag {
  private final Logger log = Logger.getLogger("PrincipalTag");

  /** The type of principal to be retrieved, or null if the default principal should be used. */
  String getType(Map params) {
    return getParam(params, "type");
  }

  /**
   * The property name to retrieve of the principal, or null if the <tt>toString()</tt> value should
   * be used.
   */
  String getProperty(Map params) {
    return getParam(params, "property");
  }

  @SuppressWarnings("unchecked")
  @Override
  public void render(Environment env, Map params, TemplateDirectiveBody body)
      throws IOException, TemplateException {
    String result = null;

    if (getSubject() != null) {
      // Get the principal to print out
      Object principal;

      if (this.getType(params) == null) {
        principal = getSubject().getPrincipal();
      } else {
        principal = this.getPrincipalFromClassName(params);
      }

      // Get the string value of the principal
      if (principal != null) {
        String property = this.getProperty(params);

        if (property == null) {
          result = principal.toString();
        } else {
          result = this.getPrincipalProperty(principal, property);
        }
      }
    }

    // Print out the principal value if not null
    if (result != null) {
      try {
        env.getOut().write(result);
      } catch (IOException ex) {
        throw new TemplateException("Error writing [" + result + "] to Freemarker.", ex, env);
      }
    }
  }

  @SuppressWarnings("unchecked")
  Object getPrincipalFromClassName(Map params) {
    String type = this.getType(params);

    try {
      Class cls = Class.forName(type);

      return getSubject().getPrincipals().oneByType(cls);
    } catch (ClassNotFoundException ex) {
      log.error("Unable to find class for name [" + type + "]", ex);
    }

    return null;
  }

  String getPrincipalProperty(Object principal, String property) throws TemplateModelException {
    try {
      BeanInfo beanInfo = Introspector.getBeanInfo(principal.getClass());

      // Loop through the properties to get the string value of the specified property
      for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
        if (propertyDescriptor.getName().equals(property)) {
          Object value = propertyDescriptor.getReadMethod().invoke(principal, (Object[]) null);

          return String.valueOf(value);
        }
      }

      // property not found, throw
      throw new TemplateModelException(
          "Property ["
              + property
              + "] not found in principal of type ["
              + principal.getClass().getName()
              + "]");
    } catch (Exception ex) {
      throw new TemplateModelException(
          "Error reading property ["
              + property
              + "] from principal of type ["
              + principal.getClass().getName()
              + "]",
          ex);
    }
  }
}
Example #17
0
/**
 * A base class for wrapping a W3C DOM Node as a FreeMarker template model.
 *
 * @author <a href="mailto:[email protected]">Jonathan Revusky</a>
 * @version $Id: NodeModel.java,v 1.80 2005/06/22 11:33:31 ddekany Exp $
 */
public abstract class NodeModel
    implements TemplateNodeModel,
        TemplateHashModel,
        TemplateSequenceModel,
        AdapterTemplateModel,
        WrapperTemplateModel {

  static final Logger logger = Logger.getLogger("freemarker.dom");

  private static DocumentBuilderFactory docBuilderFactory;

  private static Map xpathSupportMap = Collections.synchronizedMap(new WeakHashMap());

  private static XPathSupport jaxenXPathSupport;

  private static ErrorHandler errorHandler;

  static Class xpathSupportClass;

  static {
    try {
      useDefaultXPathSupport();
    } catch (Exception e) {
      // do nothing
    }
    if (xpathSupportClass == null && logger.isWarnEnabled()) {
      logger.warn("No XPath support is available.");
    }
  }

  /** The W3C DOM Node being wrapped. */
  final Node node;

  private TemplateSequenceModel children;
  private NodeModel parent;

  /**
   * Sets the DOM Parser implementation to be used when building NodeModel objects from XML files.
   */
  public static void setDocumentBuilderFactory(DocumentBuilderFactory docBuilderFactory) {
    NodeModel.docBuilderFactory = docBuilderFactory;
  }

  /**
   * @return the DOM Parser implementation that is used when building NodeModel objects from XML
   *     files.
   */
  public static DocumentBuilderFactory getDocumentBuilderFactory() {
    if (docBuilderFactory == null) {
      docBuilderFactory = DocumentBuilderFactory.newInstance();
      docBuilderFactory.setNamespaceAware(true);
      docBuilderFactory.setIgnoringElementContentWhitespace(true);
    }
    return docBuilderFactory;
  }

  /** sets the error handler to use when parsing the document. */
  public static void setErrorHandler(ErrorHandler errorHandler) {
    NodeModel.errorHandler = errorHandler;
  }

  /**
   * Create a NodeModel from a SAX input source. Adjacent text nodes will be merged (and CDATA
   * sections are considered as text nodes).
   *
   * @param removeComments whether to remove all comment nodes (recursively) from the tree before
   *     processing
   * @param removePIs whether to remove all processing instruction nodes (recursively from the tree
   *     before processing
   */
  public static NodeModel parse(InputSource is, boolean removeComments, boolean removePIs)
      throws SAXException, IOException, ParserConfigurationException {
    DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
    if (errorHandler != null) builder.setErrorHandler(errorHandler);
    Document doc = builder.parse(is);
    if (removeComments && removePIs) {
      simplify(doc);
    } else {
      if (removeComments) {
        removeComments(doc);
      }
      if (removePIs) {
        removePIs(doc);
      }
      mergeAdjacentText(doc);
    }
    return wrap(doc);
  }

  /**
   * Create a NodeModel from an XML input source. By default, all comments and processing
   * instruction nodes are stripped from the tree.
   */
  public static NodeModel parse(InputSource is)
      throws SAXException, IOException, ParserConfigurationException {
    return parse(is, true, true);
  }

  /**
   * Create a NodeModel from an XML file.
   *
   * @param removeComments whether to remove all comment nodes (recursively) from the tree before
   *     processing
   * @param removePIs whether to remove all processing instruction nodes (recursively from the tree
   *     before processing
   */
  public static NodeModel parse(File f, boolean removeComments, boolean removePIs)
      throws SAXException, IOException, ParserConfigurationException {
    DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
    if (errorHandler != null) builder.setErrorHandler(errorHandler);
    Document doc = builder.parse(f);
    if (removeComments) {
      removeComments(doc);
    }
    if (removePIs) {
      removePIs(doc);
    }
    mergeAdjacentText(doc);
    return wrap(doc);
  }

  /**
   * Create a NodeModel from an XML file. By default, all comments and processing instruction nodes
   * are stripped from the tree.
   */
  public static NodeModel parse(File f)
      throws SAXException, IOException, ParserConfigurationException {
    return parse(f, true, true);
  }

  protected NodeModel(Node node) {
    this.node = node;
  }

  /** @return the underling W3C DOM Node object that this TemplateNodeModel is wrapping. */
  public Node getNode() {
    return node;
  }

  public TemplateModel get(String key) throws TemplateModelException {
    if (key.startsWith("@@")) {
      if (key.equals("@@text")) {
        return new SimpleScalar(getText(node));
      }
      if (key.equals("@@namespace")) {
        String nsURI = node.getNamespaceURI();
        return nsURI == null ? null : new SimpleScalar(nsURI);
      }
      if (key.equals("@@local_name")) {
        String localName = node.getLocalName();
        if (localName == null) {
          localName = getNodeName();
        }
        return new SimpleScalar(localName);
      }
      if (key.equals("@@markup")) {
        StringBuilder buf = new StringBuilder();
        NodeOutputter nu = new NodeOutputter(node);
        nu.outputContent(node, buf);
        return new SimpleScalar(buf.toString());
      }
      if (key.equals("@@nested_markup")) {
        StringBuilder buf = new StringBuilder();
        NodeOutputter nu = new NodeOutputter(node);
        nu.outputContent(node.getChildNodes(), buf);
        return new SimpleScalar(buf.toString());
      }
      if (key.equals("@@qname")) {
        String qname = getQualifiedName();
        return qname == null ? null : new SimpleScalar(qname);
      }
    }
    XPathSupport xps = getXPathSupport();
    if (xps != null) {
      return xps.executeQuery(node, key);
    } else {
      throw new TemplateModelException(
          "Can't try to resolve the XML query key, because no XPath support is available. "
              + "It's either malformed or an XPath expression: "
              + key);
    }
  }

  public TemplateNodeModel getParentNode() {
    if (parent == null) {
      Node parentNode = node.getParentNode();
      if (parentNode == null) {
        if (node instanceof Attr) {
          parentNode = ((Attr) node).getOwnerElement();
        }
      }
      parent = wrap(parentNode);
    }
    return parent;
  }

  public TemplateSequenceModel getChildNodes() {
    if (children == null) {
      children = new NodeListModel(node.getChildNodes(), this);
    }
    return children;
  }

  public final String getNodeType() throws TemplateModelException {
    short nodeType = node.getNodeType();
    switch (nodeType) {
      case Node.ATTRIBUTE_NODE:
        return "attribute";
      case Node.CDATA_SECTION_NODE:
        return "text";
      case Node.COMMENT_NODE:
        return "comment";
      case Node.DOCUMENT_FRAGMENT_NODE:
        return "document_fragment";
      case Node.DOCUMENT_NODE:
        return "document";
      case Node.DOCUMENT_TYPE_NODE:
        return "document_type";
      case Node.ELEMENT_NODE:
        return "element";
      case Node.ENTITY_NODE:
        return "entity";
      case Node.ENTITY_REFERENCE_NODE:
        return "entity_reference";
      case Node.NOTATION_NODE:
        return "notation";
      case Node.PROCESSING_INSTRUCTION_NODE:
        return "pi";
      case Node.TEXT_NODE:
        return "text";
    }
    throw new TemplateModelException(
        "Unknown node type: " + nodeType + ". This should be impossible!");
  }

  public TemplateModel exec(List args) throws TemplateModelException {
    if (args.size() != 1) {
      throw new TemplateModelException("Expecting exactly one arguments");
    }
    String query = (String) args.get(0);
    // Now, we try to behave as if this is an XPath expression
    XPathSupport xps = getXPathSupport();
    if (xps == null) {
      throw new TemplateModelException("No XPath support available");
    }
    return xps.executeQuery(node, query);
  }

  public final int size() {
    return 1;
  }

  public final TemplateModel get(int i) {
    return i == 0 ? this : null;
  }

  public String getNodeNamespace() {
    int nodeType = node.getNodeType();
    if (nodeType != Node.ATTRIBUTE_NODE && nodeType != Node.ELEMENT_NODE) {
      return null;
    }
    String result = node.getNamespaceURI();
    if (result == null && nodeType == Node.ELEMENT_NODE) {
      result = "";
    } else if ("".equals(result) && nodeType == Node.ATTRIBUTE_NODE) {
      result = null;
    }
    return result;
  }

  public final int hashCode() {
    return node.hashCode();
  }

  public boolean equals(Object other) {
    if (other == null) return false;
    return other.getClass() == this.getClass() && ((NodeModel) other).node.equals(this.node);
  }

  public static NodeModel wrap(Node node) {
    if (node == null) {
      return null;
    }
    NodeModel result = null;
    switch (node.getNodeType()) {
      case Node.DOCUMENT_NODE:
        result = new DocumentModel((Document) node);
        break;
      case Node.ELEMENT_NODE:
        result = new ElementModel((Element) node);
        break;
      case Node.ATTRIBUTE_NODE:
        result = new AttributeNodeModel((Attr) node);
        break;
      case Node.CDATA_SECTION_NODE:
      case Node.COMMENT_NODE:
      case Node.TEXT_NODE:
        result = new CharacterDataNodeModel((org.w3c.dom.CharacterData) node);
        break;
      case Node.PROCESSING_INSTRUCTION_NODE:
        result = new PINodeModel((ProcessingInstruction) node);
        break;
      case Node.DOCUMENT_TYPE_NODE:
        result = new DocumentTypeModel((DocumentType) node);
        break;
    }
    return result;
  }

  /**
   * Recursively removes all comment nodes from the subtree.
   *
   * @see #simplify
   */
  public static void removeComments(Node node) {
    NodeList children = node.getChildNodes();
    int i = 0;
    int len = children.getLength();
    while (i < len) {
      Node child = children.item(i);
      if (child.hasChildNodes()) {
        removeComments(child);
        i++;
      } else {
        if (child.getNodeType() == Node.COMMENT_NODE) {
          node.removeChild(child);
          len--;
        } else {
          i++;
        }
      }
    }
  }

  /**
   * Recursively removes all processing instruction nodes from the subtree.
   *
   * @see #simplify
   */
  public static void removePIs(Node node) {
    NodeList children = node.getChildNodes();
    int i = 0;
    int len = children.getLength();
    while (i < len) {
      Node child = children.item(i);
      if (child.hasChildNodes()) {
        removePIs(child);
        i++;
      } else {
        if (child.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
          node.removeChild(child);
          len--;
        } else {
          i++;
        }
      }
    }
  }

  /**
   * Merges adjacent text/cdata nodes, so that there are no adjacent text/cdata nodes. Operates
   * recursively on the entire subtree. You thus lose information about any CDATA sections occurring
   * in the doc.
   *
   * @see #simplify
   */
  public static void mergeAdjacentText(Node node) {
    Node child = node.getFirstChild();
    while (child != null) {
      if (child instanceof Text || child instanceof CDATASection) {
        Node next = child.getNextSibling();
        if (next instanceof Text || next instanceof CDATASection) {
          String fullText = child.getNodeValue() + next.getNodeValue();
          ((CharacterData) child).setData(fullText);
          node.removeChild(next);
        }
      } else {
        mergeAdjacentText(child);
      }
      child = child.getNextSibling();
    }
  }

  /**
   * Removes comments and processing instruction, and then unites adjacent text nodes. Note that
   * CDATA sections count as text nodes.
   */
  public static void simplify(Node node) {
    NodeList children = node.getChildNodes();
    int i = 0;
    int len = children.getLength();
    Node prevTextChild = null;
    while (i < len) {
      Node child = children.item(i);
      if (child.hasChildNodes()) {
        simplify(child);
        prevTextChild = null;
        i++;
      } else {
        int type = child.getNodeType();
        if (type == Node.PROCESSING_INSTRUCTION_NODE) {
          node.removeChild(child);
          len--;
        } else if (type == Node.COMMENT_NODE) {
          node.removeChild(child);
          len--;
        } else if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) {
          if (prevTextChild != null) {
            CharacterData ptc = (CharacterData) prevTextChild;
            ptc.setData(ptc.getNodeValue() + child.getNodeValue());
            node.removeChild(child);
            len--;
          } else {
            prevTextChild = child;
            i++;
          }
        } else {
          prevTextChild = null;
          i++;
        }
      }
    }
  }

  NodeModel getDocumentNodeModel() {
    if (node instanceof Document) {
      return this;
    } else {
      return wrap(node.getOwnerDocument());
    }
  }

  /**
   * Tells the system to use (restore) the default (initial) XPath system used by this FreeMarker
   * version on this system.
   */
  public static void useDefaultXPathSupport() {
    xpathSupportClass = null;
    jaxenXPathSupport = null;
    try {
      useXalanXPathSupport();
    } catch (Exception e) {; // ignore
    }
    if (xpathSupportClass == null)
      try {
        useSunInternalXPathSupport();
      } catch (Exception e) {; // ignore
      }
    if (xpathSupportClass == null)
      try {
        useJaxenXPathSupport();
      } catch (Exception e) {; // ignore
      }
  }

  /**
   * Convenience method. Tells the system to use Jaxen for XPath queries.
   *
   * @throws Exception if the Jaxen classes are not present.
   */
  public static void useJaxenXPathSupport() throws Exception {
    Class.forName("org.jaxen.dom.DOMXPath");
    Class c = Class.forName("freemarker.ext.dom.JaxenXPathSupport");
    jaxenXPathSupport = (XPathSupport) c.newInstance();
    if (logger.isDebugEnabled()) {
      logger.debug("Using Jaxen classes for XPath support");
    }
    xpathSupportClass = c;
  }

  /**
   * Convenience method. Tells the system to use Xalan for XPath queries.
   *
   * @throws Exception if the Xalan XPath classes are not present.
   */
  public static void useXalanXPathSupport() throws Exception {
    Class.forName("org.apache.xpath.XPath");
    Class c = Class.forName("freemarker.ext.dom.XalanXPathSupport");
    if (logger.isDebugEnabled()) {
      logger.debug("Using Xalan classes for XPath support");
    }
    xpathSupportClass = c;
  }

  public static void useSunInternalXPathSupport() throws Exception {
    Class.forName("com.sun.org.apache.xpath.internal.XPath");
    Class c = Class.forName("freemarker.ext.dom.SunInternalXalanXPathSupport");
    if (logger.isDebugEnabled()) {
      logger.debug("Using Sun's internal Xalan classes for XPath support");
    }
    xpathSupportClass = c;
  }

  /**
   * Set an alternative implementation of freemarker.ext.dom.XPathSupport to use as the XPath
   * engine.
   *
   * @param cl the class, or <code>null</code> to disable XPath support.
   */
  public static void setXPathSupportClass(Class cl) {
    if (cl != null && !XPathSupport.class.isAssignableFrom(cl)) {
      throw new RuntimeException(
          "Class " + cl.getName() + " does not implement freemarker.ext.dom.XPathSupport");
    }
    xpathSupportClass = cl;
  }

  /**
   * Get the currently used freemarker.ext.dom.XPathSupport used as the XPath engine. Returns <code>
   * null</code> if XPath support is disabled.
   */
  public static Class getXPathSupportClass() {
    return xpathSupportClass;
  }

  private static String getText(Node node) {
    String result = "";
    if (node instanceof Text || node instanceof CDATASection) {
      result = ((org.w3c.dom.CharacterData) node).getData();
    } else if (node instanceof Element) {
      NodeList children = node.getChildNodes();
      for (int i = 0; i < children.getLength(); i++) {
        result += getText(children.item(i));
      }
    } else if (node instanceof Document) {
      result = getText(((Document) node).getDocumentElement());
    }
    return result;
  }

  XPathSupport getXPathSupport() {
    if (jaxenXPathSupport != null) {
      return jaxenXPathSupport;
    }
    XPathSupport xps = null;
    Document doc = node.getOwnerDocument();
    if (doc == null) {
      doc = (Document) node;
    }
    synchronized (doc) {
      WeakReference ref = (WeakReference) xpathSupportMap.get(doc);
      if (ref != null) {
        xps = (XPathSupport) ref.get();
      }
      if (xps == null) {
        try {
          xps = (XPathSupport) xpathSupportClass.newInstance();
          xpathSupportMap.put(doc, new WeakReference(xps));
        } catch (Exception e) {
          logger.error("Error instantiating xpathSupport class", e);
        }
      }
    }
    return xps;
  }

  String getQualifiedName() throws TemplateModelException {
    return getNodeName();
  }

  public Object getAdaptedObject(Class hint) {
    return node;
  }

  public Object getWrappedObject() {
    return node;
  }
}
/**
 * A {@link TemplateLoader} that uses streams reachable through {@link
 * ServletContext#getResource(String)} as its source of templates.
 */
public class WebappTemplateLoader implements TemplateLoader {

  private static final Logger LOG = Logger.getLogger("freemarker.cache");

  private final ServletContext servletContext;
  private final String subdirPath;

  private Boolean urlConnectionUsesCaches;

  private boolean attemptFileAccess = true;

  /**
   * Creates a resource template cache that will use the specified servlet context to load the
   * resources. It will use the base path of <code>"/"</code> meaning templates will be resolved
   * relative to the servlet context root location.
   *
   * @param servletContext the servlet context whose {@link ServletContext#getResource(String)} will
   *     be used to load the templates.
   */
  public WebappTemplateLoader(ServletContext servletContext) {
    this(servletContext, "/");
  }

  /**
   * Creates a template loader that will use the specified servlet context to load the resources. It
   * will use the specified base path, which is interpreted relatively to the context root (does not
   * mater if you start it with "/" or not). Path components should be separated by forward slashes
   * independently of the separator character used by the underlying operating system.
   *
   * @param servletContext the servlet context whose {@link ServletContext#getResource(String)} will
   *     be used to load the templates.
   * @param subdirPath the base path to template resources.
   */
  public WebappTemplateLoader(ServletContext servletContext, String subdirPath) {
    if (servletContext == null) {
      throw new IllegalArgumentException("servletContext == null");
    }
    if (subdirPath == null) {
      throw new IllegalArgumentException("path == null");
    }

    subdirPath = subdirPath.replace('\\', '/');
    if (!subdirPath.endsWith("/")) {
      subdirPath += "/";
    }
    if (!subdirPath.startsWith("/")) {
      subdirPath = "/" + subdirPath;
    }
    this.subdirPath = subdirPath;
    this.servletContext = servletContext;
  }

  public Object findTemplateSource(String name) throws IOException {
    String fullPath = subdirPath + name;

    if (attemptFileAccess) {
      // First try to open as plain file (to bypass servlet container resource caches).
      try {
        String realPath = servletContext.getRealPath(fullPath);
        if (realPath != null) {
          File file = new File(realPath);
          if (file.canRead() && file.isFile()) {
            return file;
          }
        }
      } catch (SecurityException e) {; // ignore
      }
    }

    // If it fails, try to open it with servletContext.getResource.
    URL url = null;
    try {
      url = servletContext.getResource(fullPath);
    } catch (MalformedURLException e) {
      LOG.warn("Could not retrieve resource " + StringUtil.jQuoteNoXSS(fullPath), e);
      return null;
    }
    return url == null ? null : new URLTemplateSource(url, getURLConnectionUsesCaches());
  }

  public long getLastModified(Object templateSource) {
    if (templateSource instanceof File) {
      return ((File) templateSource).lastModified();
    } else {
      return ((URLTemplateSource) templateSource).lastModified();
    }
  }

  public Reader getReader(Object templateSource, String encoding) throws IOException {
    if (templateSource instanceof File) {
      return new InputStreamReader(new FileInputStream((File) templateSource), encoding);
    } else {
      return new InputStreamReader(((URLTemplateSource) templateSource).getInputStream(), encoding);
    }
  }

  public void closeTemplateSource(Object templateSource) throws IOException {
    if (templateSource instanceof File) {
      // Do nothing.
    } else {
      ((URLTemplateSource) templateSource).close();
    }
  }

  /**
   * Getter pair of {@link #setURLConnectionUsesCaches(Boolean)}.
   *
   * @since 2.3.21
   */
  public Boolean getURLConnectionUsesCaches() {
    return urlConnectionUsesCaches;
  }

  /**
   * It does the same as {@link URLTemplateLoader#setURLConnectionUsesCaches(Boolean)}; see there.
   *
   * @since 2.3.21
   */
  public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) {
    this.urlConnectionUsesCaches = urlConnectionUsesCaches;
  }

  /**
   * Show class name and some details that are useful in template-not-found errors.
   *
   * @since 2.3.21
   */
  @Override
  public String toString() {
    return TemplateLoaderUtils.getClassNameForToString(this)
        + "(subdirPath="
        + StringUtil.jQuote(subdirPath)
        + ", servletContext={contextPath="
        + StringUtil.jQuote(getContextPath())
        + ", displayName="
        + StringUtil.jQuote(servletContext.getServletContextName())
        + "})";
  }

  /**
   * Gets the context path if we are on Servlet 2.5+, or else returns failure description string.
   */
  private String getContextPath() {
    try {
      Method m =
          servletContext.getClass().getMethod("getContextPath", CollectionUtils.EMPTY_CLASS_ARRAY);
      return (String) m.invoke(servletContext, CollectionUtils.EMPTY_OBJECT_ARRAY);
    } catch (Throwable e) {
      return "[can't query before Serlvet 2.5]";
    }
  }

  /**
   * Getter pair of {@link #setAttemptFileAccess(boolean)}.
   *
   * @since 2.3.23
   */
  public boolean getAttemptFileAccess() {
    return attemptFileAccess;
  }

  /**
   * Specifies that before loading templates with {@link ServletContext#getResource(String)}, it
   * should try to load the template as {@link File}; default is {@code true}, though it's not
   * always recommended anymore. This is a workaround for the case when the servlet container
   * doesn't show template modifications after the template was already loaded earlier. But it's
   * certainly better to counter this problem by disabling the URL connection cache with {@link
   * #setURLConnectionUsesCaches(Boolean)}, which is also the default behavior with {@link
   * Configuration#setIncompatibleImprovements(freemarker.template.Version)
   * incompatible_improvements} 2.3.21 and later.
   *
   * @since 2.3.23
   */
  public void setAttemptFileAccess(boolean attemptLoadingFromFile) {
    this.attemptFileAccess = attemptLoadingFromFile;
  }
}
/** @author Attila Szegedi */
class FreeMarkerJspApplicationContext implements JspApplicationContext {
  private static final Logger logger = Logger.getLogger("freemarker.jsp");
  private static final ExpressionFactory expressionFactoryImpl =
      findExpressionFactoryImplementation();

  private final LinkedList listeners = new LinkedList();
  private final CompositeELResolver elResolver = new CompositeELResolver();
  private final CompositeELResolver additionalResolvers = new CompositeELResolver();

  {
    elResolver.add(new ImplicitObjectELResolver());
    elResolver.add(additionalResolvers);
    elResolver.add(new MapELResolver());
    elResolver.add(new ResourceBundleELResolver());
    elResolver.add(new ListELResolver());
    elResolver.add(new ArrayELResolver());
    elResolver.add(new BeanELResolver());
    elResolver.add(new ScopedAttributeELResolver());
  }

  public void addELContextListener(ELContextListener listener) {
    synchronized (listeners) {
      listeners.addLast(listener);
    }
  }

  private static ExpressionFactory findExpressionFactoryImplementation() {
    ExpressionFactory ef = tryExpressionFactoryImplementation("com.sun");
    if (ef == null) {
      ef = tryExpressionFactoryImplementation("org.apache");
      if (ef == null) {
        logger.warn("Could not find any implementation for " + ExpressionFactory.class.getName());
      }
    }
    return ef;
  }

  private static ExpressionFactory tryExpressionFactoryImplementation(String packagePrefix) {
    String className = packagePrefix + ".el.ExpressionFactoryImpl";
    try {
      Class cl = ClassUtil.forName(className);
      if (ExpressionFactory.class.isAssignableFrom(cl)) {
        logger.info(
            "Using " + className + " as implementation of " + ExpressionFactory.class.getName());
        return (ExpressionFactory) cl.newInstance();
      }
      logger.warn(
          "Class " + className + " does not implement " + ExpressionFactory.class.getName());
    } catch (ClassNotFoundException e) {
    } catch (Exception e) {
      logger.error("Failed to instantiate " + className, e);
    }
    return null;
  }

  public void addELResolver(ELResolver resolver) {
    additionalResolvers.add(resolver);
  }

  public ExpressionFactory getExpressionFactory() {
    return expressionFactoryImpl;
  }

  ELContext createNewELContext(final FreeMarkerPageContext pageCtx) {
    ELContext ctx = new FreeMarkerELContext(pageCtx);
    ELContextEvent event = new ELContextEvent(ctx);
    synchronized (listeners) {
      for (Iterator iter = listeners.iterator(); iter.hasNext(); ) {
        ELContextListener l = (ELContextListener) iter.next();
        l.contextCreated(event);
      }
    }
    return ctx;
  }

  private class FreeMarkerELContext extends ELContext {
    private final FreeMarkerPageContext pageCtx;

    FreeMarkerELContext(FreeMarkerPageContext pageCtx) {
      this.pageCtx = pageCtx;
    }

    public ELResolver getELResolver() {
      return elResolver;
    }

    public FunctionMapper getFunctionMapper() {
      return null;
    }

    public VariableMapper getVariableMapper() {
      return new VariableMapper() {
        public ValueExpression resolveVariable(String name) {
          Object obj = pageCtx.findAttribute(name);
          if (obj == null) {
            return null;
          }
          return expressionFactoryImpl.createValueExpression(obj, obj.getClass());
        }

        public ValueExpression setVariable(String name, ValueExpression value) {
          ValueExpression prev = resolveVariable(name);
          pageCtx.setAttribute(name, value.getValue(FreeMarkerELContext.this));
          return prev;
        }
      };
    }
  }
}