Package org.apache.commons.betwixt.io.read

Source Code of org.apache.commons.betwixt.io.read.ReadContext

/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* 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 org.apache.commons.betwixt.io.read;

import java.beans.IntrospectionException;

import org.apache.commons.betwixt.AttributeDescriptor;
import org.apache.commons.betwixt.BindingConfiguration;
import org.apache.commons.betwixt.ElementDescriptor;
import org.apache.commons.betwixt.Options;
import org.apache.commons.betwixt.XMLBeanInfo;
import org.apache.commons.betwixt.XMLIntrospector;
import org.apache.commons.betwixt.expression.Context;
import org.apache.commons.betwixt.expression.Updater;
import org.apache.commons.betwixt.strategy.ActionMappingStrategy;
import org.apache.commons.collections.ArrayStack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;

/** 
  * <p>Extends <code>Context</code> to provide read specific functionality.</p>
  * <p>
  * Three stacks are used to manage the reading:
  * </p>
  * <ul>
  *     <li><strong>Action mapping stack</strong> contains the {@link MappingAction}'s
  * used to execute the mapping of the current element and it's ancesters back to the
  * document root.</li>
  *     <li><strong>Result stack</strong> contains the objects which are bound
  * to the current element and to each of it's ancester's back to the root</li>
  *     <li><strong>Element mapping stack</strong> records the names of the element
  * and the classes to which they are bound</li>
  * </ul>
  * @author Robert Burrell Donkina
  * @since 0.5
  */
public class ReadContext extends Context {
;
  /** Classloader to be used to load beans during reading */
  private ClassLoader classLoader;
  /** The read specific configuration */
  private ReadConfiguration readConfiguration;
  /** Records the element path together with the locations where classes were mapped*/
  private ArrayStack elementMappingStack = new ArrayStack();
  /** Contains actions for each element */
  private ArrayStack actionMappingStack = new ArrayStack();
  /** Stack contains all beans created */
  private ArrayStack objectStack = new ArrayStack();
    /** Stack contains element descriptors */
    private ArrayStack descriptorStack = new ArrayStack();
    /** Stack contains updaters */
    private ArrayStack updaterStack = new ArrayStack();

  private Class rootClass;
    /** The <code>XMLIntrospector</code> to be used to map the xml*/
  private XMLIntrospector xmlIntrospector;

  /**
    * Constructs a <code>ReadContext</code> with the same settings
    * as an existing <code>Context</code>.
    * @param context not null
    * @param readConfiguration not null
    */
  public ReadContext(Context context, ReadConfiguration readConfiguration) {
    super(context);
    this.readConfiguration = readConfiguration;
  }

  /**
    * Constructs a <code>ReadContext</code> with standard log.
    * @param bindingConfiguration the dynamic configuration, not null
    * @param readConfiguration the extra read configuration not null
    */
  public ReadContext(
    BindingConfiguration bindingConfiguration,
    ReadConfiguration readConfiguration) {
    this(
      LogFactory.getLog(ReadContext.class),
      bindingConfiguration,
      readConfiguration);
  }

  /**
    * Base constructor
    * @param log log to this Log
    * @param bindingConfiguration the dynamic configuration, not null
    * @param readConfiguration the extra read configuration not null
    */
  public ReadContext(
    Log log,
    BindingConfiguration bindingConfiguration,
    ReadConfiguration readConfiguration) {
    super(null, log, bindingConfiguration);
    this.readConfiguration = readConfiguration;
  }

  /**
    * Constructs a <code>ReadContext</code>
    * with the same settings as an existing <code>Context</code>.
    * @param readContext not null
    */
  public ReadContext(ReadContext readContext) {
    super(readContext);
    classLoader = readContext.classLoader;
    readConfiguration = readContext.readConfiguration;
  }

  /**
   * Puts a bean into storage indexed by an (xml) ID.
   *
   * @param id the ID string of the xml element associated with the bean
   * @param bean the Object to store, not null
   */
  public void putBean(String id, Object bean) {
    getIdMappingStrategy().setReference(this, bean, id);
  }

  /**
   * Gets a bean from storage by an (xml) ID.
   *
   * @param id the ID string of the xml element associated with the bean
   * @return the Object that the ID references, otherwise null
   */
  public Object getBean(String id) {
    return getIdMappingStrategy().getReferenced(this, id);
  }

  /**
   * Clears the beans indexed by id.
   */
  public void clearBeans() {
        getIdMappingStrategy().reset();
  }

  /**
    * Gets the classloader to be used.
    * @return the classloader that should be used to load all classes, possibly null
    */
  public ClassLoader getClassLoader() {
    return classLoader;
  }

  /**
    * Sets the classloader to be used.
    * @param classLoader the ClassLoader to be used, possibly null
    */
  public void setClassLoader(ClassLoader classLoader) {
    this.classLoader = classLoader;
  }

  /**
    * Gets the <code>BeanCreationChange</code> to be used to create beans
    * when an element is mapped.
    * @return the BeanCreationChain not null
    */
  public BeanCreationChain getBeanCreationChain() {
    return readConfiguration.getBeanCreationChain();
  }

    /**
     * Gets the strategy used to define default mappings actions
     * for elements.
     * @return <code>ActionMappingStrategy</code>. not null
     */
    public ActionMappingStrategy getActionMappingStrategy() {
        return readConfiguration.getActionMappingStrategy();
    }

  /**
    * Pops the top element from the element mapping stack.
    * Also removes any mapped class marks below the top element.
    *
    * @return the name of the element popped
    * if there are any more elements on the stack, otherwise null.
    * This is the local name if the parser is namespace aware, otherwise the name
    */
  public String popElement() {
        // since the descriptor stack is populated by pushElement,
        // need to ensure that it's correct popped by popElement
        if (!descriptorStack.isEmpty()) {
            descriptorStack.pop();
        }
       
        if (!updaterStack.isEmpty()) {
            updaterStack.pop();
        }
       
        popOptions();
       
    Object top = null;
    if (!elementMappingStack.isEmpty()) {
      top = elementMappingStack.pop();
      if (top != null) {
        if (!(top instanceof String)) {
          return popElement();
        }
      }
    }

    return (String) top;
  }

    /**
     * Gets the element name for the currently mapped element.
     * @return the name of the currently mapped element,
     * or null if there has been no element mapped
     */
  public String getCurrentElement() {
      String result = null;
      int stackSize = elementMappingStack.size();
      int i = 0;
      while ( i < stackSize ) {
          Object mappedElement = elementMappingStack.peek(i);
          if (mappedElement instanceof String) {
              result  = (String) mappedElement;
              break;
          }
          ++i;
      }
      return result;
  }

  /**
    * Gets the Class that was last mapped, if there is one.
    *
    * @return the Class last marked as mapped
      * or null if no class has been mapped
    */
  public Class getLastMappedClass() {
        Class lastMapped = null;
        for (int i = 0, size = elementMappingStack.size();
            i < size;
            i++) {
            Object entry = elementMappingStack.peek(i);
            if (entry instanceof Class) {
                lastMapped = (Class) entry;
                break;
            }
        }
        return lastMapped;
  }

    private ElementDescriptor getParentDescriptor() throws IntrospectionException {
        ElementDescriptor result = null;
        if (descriptorStack.size() > 1) {
            result = (ElementDescriptor) descriptorStack.peek(1);
        }
        return result;
    }
   

  /**
    * Pushes the given element onto the element mapping stack.
    *
    * @param elementName the local name if the parser is namespace aware,
    * otherwise the full element name. Not null
    */
  public void pushElement(String elementName) throws Exception {

    elementMappingStack.push(elementName);
    // special case to ensure that root class is appropriately marked
    //TODO: is this really necessary?
        ElementDescriptor nextDescriptor = null;
    if (elementMappingStack.size() == 1 && rootClass != null) {
      markClassMap(rootClass);
            XMLBeanInfo rootClassInfo
                = getXMLIntrospector().introspect(rootClass);
            nextDescriptor = rootClassInfo.getElementDescriptor();
    } else {
            ElementDescriptor currentDescriptor = getCurrentDescriptor();
            if (currentDescriptor != null) {
                nextDescriptor = currentDescriptor.getElementDescriptor(elementName);
            }
        }
        Updater updater = null;
        Options options = null;
        if (nextDescriptor != null) {
            updater = nextDescriptor.getUpdater();
            options = nextDescriptor.getOptions();
        }
        updaterStack.push(updater);
        descriptorStack.push(nextDescriptor);
        pushOptions(options);
  }

  /**
    * Marks the element name stack with a class mapping.
    * Relative paths and last mapped class are calculated using these marks.
    *
    * @param mappedClazz the Class which has been mapped at the current path, not null
    */
  public void markClassMap(Class mappedClazz) throws IntrospectionException {
        if (mappedClazz.isArray()) {
            mappedClazz = mappedClazz.getComponentType();
        }
    elementMappingStack.push(mappedClazz);
       
        XMLBeanInfo mappedClassInfo = getXMLIntrospector().introspect(mappedClazz);
        ElementDescriptor mappedElementDescriptor = mappedClassInfo.getElementDescriptor();
        descriptorStack.push(mappedElementDescriptor);
       
        Updater updater = mappedElementDescriptor.getUpdater();
        updaterStack.push(updater);
  }

  /**
   * Pops an action mapping from the stack
   * @return <code>MappingAction</code>, not null
   */
  public MappingAction popMappingAction() {
    return (MappingAction) actionMappingStack.pop();
  }

  /**
   * Pushs an action mapping onto the stack
   * @param mappingAction
   */
  public void pushMappingAction(MappingAction mappingAction) {
    actionMappingStack.push(mappingAction);
  }

  /**
   * Gets the current mapping action
   * @return MappingAction
   */
  public MappingAction currentMappingAction() {
    if (actionMappingStack.size() == 0)
    {
      return null
    }
    return (MappingAction) actionMappingStack.peek();
  }

  public Object getBean() {
    return objectStack.peek();
  }

  public void setBean(Object bean) {
    // TODO: maybe need to deprecate the set bean method
    // and push into subclass
    // for now, do nothing   
  }

    /**
     * Pops the last mapping <code>Object</code> from the
     * stack containing beans that have been mapped.
     * @return the last bean pushed onto the stack
     */
  public Object popBean() {
    return objectStack.pop();
  }

    /**
     * Pushs a newly mapped <code>Object</code> onto the mapped bean stack.
     * @param bean
     */
  public void pushBean(Object bean) {
    objectStack.push(bean);
  }

    /**
     * Gets the <code>XMLIntrospector</code> to be used to create
     * the mappings for the xml.
     * @return <code>XMLIntrospector</code>, not null
     */
  public XMLIntrospector getXMLIntrospector() {
        // read context is not intended to be used by multiple threads
        // so no need to worry about lazy creation
        if (xmlIntrospector == null) {
            xmlIntrospector = new XMLIntrospector();
        }
    return xmlIntrospector;
  }

    /**
     * Sets the <code>XMLIntrospector</code> to be used to create
     * the mappings for the xml.
     * @param xmlIntrospector <code>XMLIntrospector</code>, not null
     */
  public void setXMLIntrospector(XMLIntrospector xmlIntrospector) {
    this.xmlIntrospector = xmlIntrospector;
  }

  public Class getRootClass() {
    return rootClass;
  }

  public void setRootClass(Class rootClass) {
    this.rootClass = rootClass;
  }

    /**
     * Gets the <code>ElementDescriptor</code> that describes the
     * mapping for the current element.
     * @return <code>ElementDescriptor</code> or null if there is no
     * current mapping
     * @throws Exception
     */
  public ElementDescriptor getCurrentDescriptor() throws Exception {
    ElementDescriptor result = null;
        if (!descriptorStack.empty()) {
            result = (ElementDescriptor) descriptorStack.peek();
        }
    return result;
  }
   
    /**
     * Populates the object mapped by the <code>AttributeDescriptor</code>s
     * with the values in the given <code>Attributes</code>.
     * @param attributeDescriptors <code>AttributeDescriptor</code>s, not null
     * @param attributes <code>Attributes</code>, not null
     */
  public void populateAttributes(
    AttributeDescriptor[] attributeDescriptors,
    Attributes attributes) {

    Log log = getLog();
    if (attributeDescriptors != null) {
      for (int i = 0, size = attributeDescriptors.length;
        i < size;
        i++) {
        AttributeDescriptor attributeDescriptor =
          attributeDescriptors[i];

        // The following isn't really the right way to find the attribute
        // but it's quite robust.
        // The idea is that you try both namespace and local name first
        // and if this returns null try the qName.
        String value =
          attributes.getValue(
            attributeDescriptor.getURI(),
            attributeDescriptor.getLocalName());

        if (value == null) {
          value =
            attributes.getValue(
              attributeDescriptor.getQualifiedName());
        }

        if (log.isTraceEnabled()) {
          log.trace("Attr URL:" + attributeDescriptor.getURI());
          log.trace(
            "Attr LocalName:" + attributeDescriptor.getLocalName());
          log.trace(value);
        }

        Updater updater = attributeDescriptor.getUpdater();
        log.trace(updater);
        if (updater != null && value != null) {
          updater.update(this, value);
        }
      }
    }
  }

    /**
     * <p>Pushes an <code>Updater</code> onto the stack.</p>
     * <p>
     * <strong>Note</strong>Any action pushing an <code>Updater</code> onto
     * the stack should take responsibility for popping
     * the updater from the stack at an appropriate time.
     * </p>
     * <p>
     * <strong>Usage:</strong> this may be used by actions
     * which require a temporary object to be updated.
     * Pushing an updater onto the stack allow actions
     * downstream to transparently update the temporary proxy.
     * </p>
     * @param updater Updater, possibly null
     */
    public void pushUpdater(Updater updater) {
        updaterStack.push(updater);
    }
   
    /**
     * Pops the top <code>Updater</code> from the stack.
     * <p>
     * <strong>Note</strong>Any action pushing an <code>Updater</code> onto
     * the stack should take responsibility for popping
     * the updater from the stack at an appropriate time.
     * </p>
     * @return <code>Updater</code>, possibly null
     */
    public Updater popUpdater() {
        return (Updater) updaterStack.pop();
    }

    /**
     * Gets the current <code>Updater</code>.
     * This may (or may not) be the updater for the current
     * descriptor.
     * If the current descriptor is a bean child,
     * the the current updater will (most likely)
     * be the updater for the property.
     * Actions (that, for example, use proxy objects)
     * may push updaters onto the stack.
     * @return Updater, possibly null
     */
    public Updater getCurrentUpdater() {
        // TODO: think about whether this is right
        //       it makes some sense to look back up the
        //       stack until a non-empty updater is found.
        //       actions who need to put a stock to this
        //       behaviour can always use an ignoring implementation.
        Updater result = null;
        if (!updaterStack.empty()) {
            result = (Updater) updaterStack.peek();
            if ( result == null && updaterStack.size() >1 ) {
                result = (Updater) updaterStack.peek(1);
            }
        }
        return result; 
    }



}
TOP

Related Classes of org.apache.commons.betwixt.io.read.ReadContext

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.