Package org.apache.cocoon.el.impl.objectmodel

Source Code of org.apache.cocoon.el.impl.objectmodel.ObjectModelImpl$StackReversedIteration

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.cocoon.el.impl.objectmodel;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;

import org.apache.cocoon.el.objectmodel.ObjectModel;
import org.apache.cocoon.el.objectmodel.ObjectModelProvider;
import org.apache.commons.collections.ArrayStack;
import org.apache.commons.collections.KeyValue;
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.iterators.ReverseListIterator;
import org.apache.commons.collections.keyvalue.DefaultKeyValue;
import org.apache.commons.collections.map.AbstractMapDecorator;
import org.apache.commons.collections.map.MultiValueMap;
import org.apache.commons.jxpath.DynamicPropertyHandler;
import org.apache.commons.jxpath.JXPathBeanInfo;
import org.apache.commons.jxpath.JXPathIntrospector;

/**
* Prototype implementation of {@link ObjectModel} interface. It <b>must</b> be initialized manually for now.
*
*/
public class ObjectModelImpl extends AbstractMapDecorator implements ObjectModel {
    //FIXME: It seems that there is no easy way to reuse MuliValueMap

    private static final String SEGMENT_SEPARATOR = "/";

    private ArrayStack localContexts;
    private Map singleValueMap;
    private MultiMap multiValueMap;
    private MultiMap multiValueMapForLocated;
    private Map initialEntries;

    //FIXME: This is a temporary solution
    private boolean modified;


    public ObjectModelImpl() {
        singleValueMap = new HashMap();
        //FIXME: Not sure if this makes sense
        //super.map = UnmodifiableMap.decorate(singleValueMap);
        super.map = singleValueMap;
        localContexts = new ArrayStack();
        multiValueMap = MultiValueMap.decorate(new HashMap(), StackReversedIteration.class);
        multiValueMapForLocated = MultiValueMap.decorate(new HashMap(), StackReversedIteration.class);
    }

    public static class StackReversedIteration extends ArrayStack {

        public Iterator iterator() {
            return new ReverseListIterator(this);
        }

        public ListIterator listIterator() {
            throw new UnsupportedOperationException();
        }
    }

    public Object get(Object key) {
        //FIXME: This should be done more elegantly
        if ("this".equals(key)) {
            return this;
        }

        return super.get(key);
    }

    public MultiMap getAll() {
        return UnmodifiableMultiMap.decorate(multiValueMap);
    }

    public Object put(Object key, Object value) {
        modified = true;
        if (!localContexts.empty()) {
            ((ArrayStack) localContexts.peek()).push(new DefaultKeyValue(key, value));
        }

        singleValueMap.put(key, value);
        multiValueMap.put(key, value);

        return value;
    }

    public void putAll(Map mapToCopy) {
        modified = true;
        if (!localContexts.empty()) {
            ArrayStack entries = (ArrayStack)localContexts.peek();
            for (Iterator keysIterator = mapToCopy.keySet().iterator(); keysIterator.hasNext();) {
                Object key = keysIterator.next();
                entries.push(new DefaultKeyValue(key, mapToCopy.get(key)));
            }
        }

        singleValueMap.putAll(mapToCopy);
        multiValueMap.putAll(mapToCopy);
    }

    /**
     * Locates map at given path
     * @param path where Map can be found
     * @param createIfNeeded indicates if map(s) should be created if no corresponding found
     * @return located Map or null if <code>createIfNeeded</code> is false and Map cannot be found
     */
    private Map locateMapAt(String path, boolean createIfNeeded) {
        if (path.lastIndexOf(SEGMENT_SEPARATOR) == -1) {
            return this;
        }

        Map map = this;
        int segmentBegin = 0;
        int segmentEnd = path.indexOf(SEGMENT_SEPARATOR);
        while (segmentEnd != -1) {
            String key = path.substring(segmentBegin, segmentEnd);
            if (map.containsKey(key)) {
                Object obj = map.get(key);
                if (!(obj instanceof Map)) {
                    throw new ClassCastException("Object at path " + path.substring(0, segmentEnd) + "is not a Map");
                }

                map = (Map)obj;
            } else {
                if (!createIfNeeded) {
                    return null;
                }

                Map newMap = new HashMap();
                map.put(key, newMap);
                map = newMap;
            }
            segmentBegin = segmentEnd + 1;
            segmentEnd = path.indexOf(SEGMENT_SEPARATOR, segmentBegin);
        }

        return map;
    }

    public void putAt(String path, Object value) {
        if (path == null) {
            throw new NullPointerException("Path cannot be null.");
        }
        if (path.length() == 0) {
            throw new IllegalArgumentException("Path cannot be empty");
        }

        Map map = locateMapAt(path, true);
        String key = path.substring(path.lastIndexOf(SEGMENT_SEPARATOR) + 1, path.length());
        if (!localContexts.empty()) {
            ((ArrayStack) localContexts.peek()).push(new PathValue(path, value));
        }
        map.put(key, value);
    }

    private void removeAt(String path, Object value) {
        if (path == null) {
            throw new NullPointerException("Path cannot be null.");
        }
        if (path.length() == 0) {
            throw new IllegalArgumentException("Path cannot be empty");
        }

        Map map = locateMapAt(path, false);
        String key = path.substring(path.lastIndexOf(SEGMENT_SEPARATOR) + 1, path.length());
        if (map == null) {
            return;
        }

        multiValueMapForLocated.remove(key, value);
        if (multiValueMap.containsKey(key)) {
            map.put(key, ((StackReversedIteration) multiValueMap.get(key)).peek());
        } else {
            map.remove(key);
        }
    }

    public void cleanupLocalContext() {
        if (localContexts.empty()) {
            throw new IllegalStateException("Local contexts stack is empty");
        }

        ArrayStack removeEntries = (ArrayStack)localContexts.pop();
        while (!removeEntries.isEmpty()) {
            if (removeEntries.peek() instanceof PathValue) {
                PathValue entry = (PathValue)removeEntries.pop();
                removeAt(entry.getPath(), entry.getValue());
            } else {
                KeyValue entry = (KeyValue)removeEntries.pop();
                Object key = entry.getKey();
                Object value = entry.getValue();

                multiValueMap.remove(key, value);
                if (multiValueMap.containsKey(key)) {
                    singleValueMap.put(key, ((StackReversedIteration) multiValueMap.get(key)).peek());
                } else {
                    singleValueMap.remove(key);
                }
            }
        }
    }

    public void markLocalContext() {
        localContexts.push(new ArrayStack());
    }

    public Map getInitialEntries() {
        return initialEntries;
    }

    public void setInitialEntries(Map initialEntries) {
        if (this.initialEntries != null) {
            throw new IllegalStateException("Object Model has initial entries set already.");
        }

        this.initialEntries = initialEntries;
        for (Iterator keysIterator = initialEntries.keySet().iterator(); keysIterator.hasNext(); ) {
            Object key = keysIterator.next();
            put(key, ((ObjectModelProvider)initialEntries.get(key)).getObject());
        }

        this.modified = false;
    }

    public void fillContext() {
        // Hack: I use jxpath to populate the context object's properties
        // in the jexl context
        Object contextObject = get(CONTEXTBEAN);
        if (contextObject == null) {
            //nothing to do
            return;
        }

        // FIXME Exception Handling
        final JXPathBeanInfo bi =
            JXPathIntrospector.getBeanInfo(contextObject.getClass());
        if (bi.isDynamic()) {
            Class cl = bi.getDynamicPropertyHandlerClass();
            try {
                DynamicPropertyHandler h =
                    (DynamicPropertyHandler) cl.newInstance();
                String[] result = h.getPropertyNames(contextObject);
                int len = result.length;
                for (int i = 0; i < len; i++) {
                    try {
                        put(result[i], h.getProperty(contextObject, result[i]));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            PropertyDescriptor[] props =  bi.getPropertyDescriptors();
            int len = props.length;
            for (int i = 0; i < len; i++) {
                try {
                    Method read = props[i].getReadMethod();
                    if (read != null) {
                        put(props[i].getName(),
                            read.invoke(contextObject, null));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private final class PathValue {
        private String path;
        private Object value;

        public PathValue(String path, Object value) {
            this.path = path;
            this.value = value;
        }

        public String getPath() {
            return this.path;
        }

        public Object getValue() {
            return this.value;
        }

    }

    /* (non-Javadoc)
     * @see ObjectModel#setParent(ObjectModel)
     */
    public void setParent(ObjectModel parentObjectModel) {
        if (this.modified) {
            throw new IllegalStateException("Setting parent may occur only if Object Model is empty.");
        }

        singleValueMap.putAll(parentObjectModel);
        multiValueMap.putAll(parentObjectModel.getAll());
    }
}
TOP

Related Classes of org.apache.cocoon.el.impl.objectmodel.ObjectModelImpl$StackReversedIteration

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.