Package de.odysseus.calyxo.base.conf.impl

Source Code of de.odysseus.calyxo.base.conf.impl.ConfigImpl$Elements

/*
* Copyright 2004, 2005, 2006 Odysseus Software GmbH
*
* Licensed 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 de.odysseus.calyxo.base.conf.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import javax.servlet.jsp.el.ELException;
import javax.servlet.jsp.el.ExpressionEvaluator;
import javax.servlet.jsp.el.FunctionMapper;
import javax.servlet.jsp.el.VariableResolver;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import de.odysseus.calyxo.base.ModuleContext;
import de.odysseus.calyxo.base.conf.Config;
import de.odysseus.calyxo.base.conf.ConfigException;
import de.odysseus.calyxo.base.util.PropertyUtils;

/**
* Base configuration element.
*
* @author Oliver Stuhr
* @author Christoph Beck
*/
public abstract class ConfigImpl implements Config {
  protected static final Log log = LogFactory.getLog(ConfigImpl.class);
 
  private static final String[] EMPTY_STRING_ARRAY = new String[0];

  interface ConfigImplInitializer {
    public void init(ConfigImpl node) throws ConfigException;
  }

  private static ConfigImplInitializer initCaller = new ConfigImplInitializer() {
    public void init(ConfigImpl node) throws ConfigException {
      node._init();
      if (!node.initCalled) {
        log.warn("Node " + node.toInlineString() + " may not have been fully initialized! Check super._init() calls...");
      }
    }
  };

  private static ConfigImplInitializer init2Caller = new ConfigImplInitializer() {
    public void init(ConfigImpl node) throws ConfigException {
      node._init2();
      if (!node.init2Called) {
        log.warn("Node " + node.toInlineString() + " may not have been fully initialized! Check super._init2() calls...");
      }
    }
  };

  /**
   * Collect attributes from an element
   */
  protected static class Attributes implements Serializable {
    private StringBuffer buffer = new StringBuffer();
    private boolean closed;
    /**
     * Add attribute key/value pair
     */
    public void put(String key, Object value) {
      if (value != null) {
        if (!isEmpty()) {
          buffer.append(" ");
        }
        buffer.append(key);
        buffer.append("=\"");
        buffer.append(value);
        buffer.append('"');
      }
    }
    /**
     * Append attribute list to specified buffer.
     * This will append a string of the form
     * <code>key1="value1" key2="value2"</code> ...
     */
    void appendTo(StringBuffer s) {
      s.append(buffer.toString());
    }
    /**
     * Answer true iff no attributes have been added yet
     */
    public boolean isEmpty() {
      return buffer.length() == 0;
    }
    /**
     * Answer string representation
     */
    public String toString() {
      return buffer.toString();
    }
    /**
     * Indicate that all additions have been done.
     */
    void close() {
      closed = true;
    }
    /**
     * Answer <code>true</code> iff <code>close()</code> has been called.
     */
    boolean isClosed() {
      return closed;
    }
  }

  /**
   * Collect elements (used for children list)
   */
  protected static class Elements implements Serializable {
    private ArrayList list;
    private boolean closed;
    /**
     * Answer true iff no elements have been added yet
     */
    public boolean isEmpty() {
      return list == null || list.isEmpty();
    }
    /**
     * Add an element
     */
    public void add(Config element) {
      if (element != null) {
        ConfigImpl config = (ConfigImpl)element;
        if (list == null)
          list = new ArrayList();
        list.add(config);
      }
    }
    /**
     * Add elements
     */
    public void add(Iterator elements) {
      while (elements.hasNext()) {
        add((ConfigImpl)elements.next());
      }
    }
    /**
     * Answer elements (instanceof <code>ConfigImpl)
     */
    Iterator iterator() {
      return isEmpty() ? Collections.EMPTY_LIST.iterator() : list.iterator();
    }
    /**
     * Indicate that all additions have been done.
     */
    void close() {
      closed = true;
    }
    /**
     * Answer <code>true</code> iff <code>close()</code> has been called.
     */
    boolean isClosed() {
      return closed;
    }
  }

  private static final String TAB = "  ";

  private ConfigImpl previous;
  private ConfigImpl parent;
  private Elements children = new Elements();
  private Attributes attributes = new Attributes();
  private FunctionMapper _parentFunctionMapper;

  private boolean resolveCalled = false;
  private boolean initCalled = false;
  private boolean init2Called = false;

  /**
   * Answer the element's name.
   */
  protected String _getElementName() {
    return "unknown";
  }

  /**
   * Add the element's attributes to specifies <code>Attributes</code>.
   */
  protected void _putAttributes(Attributes attributes) {
    attributes.close();
  }

  /**
   * Add the elements children to specified <code>Elements</code>
   */
  protected void _addChildren(Elements elements) {
    elements.close();
  }

  /**
   * Answer an array of the element's dynamic property names.
   * Dynamic properties may contain variable references in their
   * property values, as in <code>/WEB-INF/${layout}/page.jspx</code>.
   * These references will be resolved automatically during initialization.
   */
  protected String[] _getDynamicAttributes() {
    return EMPTY_STRING_ARRAY;
  }

  /**
   * Get the parent element
   */
  protected final ConfigImpl _getParent() {
    return parent;
  }

  /**
   * Get the previous sibling element
   */
  protected final ConfigImpl _getPrevious() {
    return previous;
  }

  /**
   * Get the children elements
   */
  protected Iterator _getChildren() {
    return children.iterator();
  }

  /**
   * Search nearest element on the path towards the root element
   * (including the element itself) assignable to specified type.
   * @param type element type to find
   * @return nearest ancestor element or the element itself
   * assignable to specified type
   */
  protected ConfigImpl _getNearestAncestorOrSelf(Class type) {
    if (type.isAssignableFrom(getClass())) {
      return this;
    }
    return _getNearestAncestor(type);
  }

  /**
   * Search nearest element on the path towards the root element
   * (excluding the element itself) assignable to specified type.
   * @param type element type to find
   * @return nearest ancestor node assignable to specified type
   */
  protected ConfigImpl _getNearestAncestor(Class type) {
    return parent == null ? null : parent._getNearestAncestorOrSelf(type);
  }

  /**
   * Initialize element.
   * When this method is called, <code>_init</code> has been called for
   * all nodes before the receiver (according to preorder).
   * To be overwritten by subclasses.
   * @throws ConfigException
   */
  protected void _init() throws ConfigException {
    initCalled = true;
  }

  /**
   * Initialize element.
   * When this method is called, <code>_init2</code> has been called for
   * all nodes before the receiver (according to preorder).
   * Also, <code>_init</code> has been called for all nodes.
   * To be overwritten by subclasses.
   * @throws ConfigException
   */
  protected void _init2() throws ConfigException {
    init2Called = true;
  }

  /**
   * Log warning if base implementation of <code>_init</code> or
   * <code>_init2</code> has not been called (missing call to super...)
   *
   */
  void checkInitCalled() {
    if (!initCalled) {
      log.warn("Node " + toInlineString() + " may not have been fully initialized! Check super._init() calls...");
    }
    if (!init2Called) {
      log.warn("Node " + toInlineString() + " may not have been fully initialized! Check super._init2() calls...");
    }
  }

  /**
   * Delegate to parent
   * @see javax.servlet.jsp.el.FunctionMapper#resolveFunction(java.lang.String, java.lang.String)
   */
  public Method resolveFunction(String prefix, String name) {
    return _parentFunctionMapper == null ? null : _parentFunctionMapper.resolveFunction(prefix, name);
  }

  /**
   * Evaluate other than dynamic attributes.
   * This method is called after <code>_resolve()</code> has been
   * called for the subtree rooted at this node.
   * @throws ConfigException
   */
  protected void _evaluate(ModuleContext context) throws ConfigException {
  }

  /**
   * Resolve values of dynamic properties. The properties are accessed
   * using reflection.
   * @throws ConfigException if a variable could not been resolved on
   * property access error.
   */
  protected final void _resolveTree(ModuleContext context) throws ConfigException {
    linkSubtree();
    resolveSubtree(context);
  }

  /**
   * Initialize tree.
   * Call <code>_init()</code> for the elements in the subtree
   * rooted at this element.
   * @throws ConfigException from an element's <code>_init()</code> method.
   */
  protected final void _initTree() throws ConfigException {
    linkSubtree();
    initSubtree(initCaller);
    initSubtree(init2Caller);
  }

  private void linkSubtree() throws ConfigException {
    _addChildren(children = new Elements());
    if (!children.isClosed()) {
      log.warn("Node " + toInlineString() + " may not have been fully initialized! Check super._addChildren() calls...");
    }
    _putAttributes(attributes = new Attributes());
    if (!attributes.isClosed()) {
      log.warn("Node " + toInlineString() + " may not have been fully initialized! Check super._putAttributes() calls...");
    }
    ConfigImpl previous = null;
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      ConfigImpl child = (ConfigImpl)iterator.next();
      child.previous = previous;
      child.parent = this;
      previous = child;
      child.linkSubtree();
    }
  }

  private void resolveSubtree(ModuleContext context) throws ConfigException {
    VariableResolver variables = new ConfigImplVariableResolver(context, this);
    _parentFunctionMapper = parent; // save file parent (may change after merge)
    _resolve(context.getExpressionEvaluator(), variables);
    if (!resolveCalled) {
      log.warn("Node " + toInlineString() + " may not have been fully initialized! Check super._resolve() calls...");
    }
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      ConfigImpl child = (ConfigImpl)iterator.next();
      child.resolveSubtree(context);
    }
    _evaluate(context);
  }

  /**
   * Resolve dynamic attribute expressions.
   * This implementation uses <code>_getDynamicAttributes()</code>
   * to get a list of attributes whose values may have expressions.
   * It reads the corresponding property values, evaluates them
   * and finally writes them back.
   * <p/>
   * Subclasses may override this method to do any special handling
   * or to use the evaluator, variable resolver and function mapper
   * to do further expression evaluations.
   * @param evaluator expression evaluator
   * @param variables the variables resolver
   * @throws ConfigException
   */
  protected void _resolve(ExpressionEvaluator evaluator, VariableResolver variables) throws ConfigException {
    String[] dynamicAttributes = _getDynamicAttributes();
    for (int i = 0; i < dynamicAttributes.length; i++) {
      String attribute = dynamicAttributes[i];
      String value = null;
      try {
        value = (String)PropertyUtils.getProperty(this, attribute);
        if (value != null && value.indexOf("${") >= 0) {
          Object object = evaluator.evaluate(value, Object.class, variables, this);
          Class type = PropertyUtils.getPropertyDescriptor(getClass(), attribute).getPropertyType();
          if (type == String.class) {
            if (object != null) {
              object = object.toString();
            }
          }
          PropertyUtils.setProperty(this, attribute, object);
        }
      } catch (ELException e) {
        throw new ConfigException("Could not evaluate " + toInlineString() + "/@" + attribute + " '" + value + "'", e);
      } catch (Exception e) {
        throw new ConfigException("Could not access " + toInlineString() + "/@" + attribute, e);
      }
    }
    resolveCalled = true;
  }


  private void initSubtree(ConfigImplInitializer initializer) throws ConfigException {
    if (log.isTraceEnabled()) {
      log.trace("Initializing node " + toInlineString() + " begin");
    }
    initializer.init(this);
    Iterator iterator = children.iterator();
    while (iterator.hasNext()) {
      ConfigImpl child = (ConfigImpl)iterator.next();
      child.initSubtree(initializer);
    }
    if (log.isTraceEnabled()) {
      log.trace("Initializing node " + toInlineString() + " end");
    }
  }

  /**
   * Append a short string representation to the specified string buffer.
   */
  protected void appendInlineString(StringBuffer s) {
    if (parent != null) {
      parent.appendInlineString(s);
    }
    s.append("/");
    s.append(_getElementName());
    Attributes attrs = new Attributes();
    _putAttributes(attrs);
    if (!attrs.isEmpty()) {
      s.append("[");
      attrs.appendTo(s);
      s.append("]");
    }
  }
 
  /**
   * Answer a short string representation.
   */
  public final String toInlineString() {
    StringBuffer s = new StringBuffer();
    appendInlineString(s);
    return s.toString();
  }

  /**
   * Lookup variable.
   * Delegate request to previous sibling (if available) or parent element
   */
  Object lookupVariable(String name) {
    if (previous != null) {
      return previous.lookupVariable(name);
    } else if (parent != null) {
      return parent.lookupVariable(name);
    } else {
      return null;
    }
  }

  private String toString(String prefix, int depth) {
    StringBuffer result = new StringBuffer();

    result.append(prefix);
    result.append("<");
    result.append(_getElementName());
    if (!attributes.isEmpty()) {
      result.append(" ");
      attributes.appendTo(result);
    }
    if (children.isEmpty())
      result.append("/>\n");
    else {
      result.append(">\n");
      if (depth == 0) {
        result.append(prefix + TAB + "...\n");
      } else {
        Iterator elements = children.iterator();
        while (elements.hasNext()) {
          ConfigImpl element = (ConfigImpl)elements.next();
          result.append(element.toString(prefix + TAB, depth - 1));
        }
      }
      result.append(prefix);
      result.append("</");
      result.append(_getElementName());
      result.append(">\n");
    }

    return result.toString();
  }

  /**
   * Answer string representation
   */
  public String toString() {
    return toString(1);
  }

  /**
   * Answer xml string representation, prune to specified depth.
   * @param depth maximal tree depth (0 indicates no children,
   * -1 indicates unlimited depth)
   */
  public String toString(int depth) {
    return toString("", depth);
  }

  /**
   * Answer xml string representation.
   */
  public String toXML() {
    return toString(-1);
  }
}
TOP

Related Classes of de.odysseus.calyxo.base.conf.impl.ConfigImpl$Elements

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.