Package org.auraframework.instance

Source Code of org.auraframework.instance.InstanceStack$Entry

/*
* Copyright (C) 2013 salesforce.com, inc.
*
* 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.auraframework.instance;

import java.io.IOException;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.auraframework.Aura;
import org.auraframework.def.DefDescriptor;
import org.auraframework.throwable.AuraRuntimeException;
import org.auraframework.util.json.Json;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
* A 'stack' of components specifying the position in the tree.
*
* This stack is used during component creation on the server to build up and
* maintain a tree position for communicating with the client. This tree
* position must exactly match, or everything breaks.
*
* Because this is such a sensitive area, we do a variety of very careful checks
* to ensure that we blow up if there is any indication that we might have
* gotten out of sync.
*
* In an ideal world, we would not have to maintain this because we would know
* our 'parentage', but that is much easier said than done.
*/
public class InstanceStack {
    public InstanceStack() {
        this.path = new StringBuilder();
        this.stack = Lists.newArrayList();
        this.current = new Entry(null, path.length());
        setAttributeName("body");
        setAttributeIndex(0);
        this.current.top = true;
        this.base = path.toString();
        this.topUnprivileged = null;
    }

    /**
     * start processing a component.
     */
    public void pushInstance(Instance<?> instance, DefDescriptor<?> desc) {
        if (topUnprivileged == null) {
            if (!Aura.getConfigAdapter().isPrivilegedNamespace(desc.getNamespace())) {
                topUnprivileged = instance;
            }
        }
        stack.add(current);
        current = new Entry(instance, path.length());
    }

    /**
     * start processing a component.
     */
    public void popInstance(Instance<?> instance) {
        if (current.instance != instance) {
            throw new AuraRuntimeException("mismatched instance pop");
        }
        if (topUnprivileged == instance) {
            topUnprivileged = null;
        }
        current = stack.remove(stack.size() - 1);
        if (current.top) {
            int index = current.index;
            clearAttributeIndex(index);
            setAttributeIndex(index + 1);
        }
    }

    /**
     * Ensure that we have the expected parent.
     *
     * This is used by parented items to ensure that their parent is on the
     * stack. This is required in the case that something is changed outside of
     * the tree traversal. In which case this routine will pre-fill the path to
     * the correct point.
     */
    public void markParent(Instance<?> parent) {
        if (!current.top) {
            if (current.instance != parent) {
                throw new AuraRuntimeException("Don't know how to handle setAttribute here");
            }
            current.count += 1;
        } else {
            path.setLength(0);
            path.append(parent.getPath());
            pushInstance(parent, parent.getDescriptor());
        }
    }

    /**
     * Clear the parent previously marked.
     */
    public void clearParent(Instance<?> parent) {
        if (current.instance != parent) {
            throw new AuraRuntimeException("mismatched clear parent");
        }
        if (current.count > 0) {
            current.count -= 1;
        } else {
            popInstance(parent);
            path.setLength(0);
            path.append(base);
        }
    }

    /**
     * set the name part on the stack.
     *
     * A name could be either an attribute name (e.g. body), or a predefined
     * name (i.e. $ for super class). Note that you _must_ clear the name after
     * setting it.
     */
    public void setAttributeName(String name) {
        if (current.name != null || current.top) {
            throw new AuraRuntimeException("Setting name illegally");
        }
        current.name = name;
        path.append("/");
        if (name.equals("body")) {
            path.append("*");
        } else if (name.equals("realbody")) {
            path.append("+");
        } else {
            path.append(name);
        }
        current.namePos = path.length();
    }

    /**
     * pop a previously pushed name off the stack.
     */
    public void clearAttributeName(String name) {
        if (!name.equals(current.name)) {
            throw new AuraRuntimeException("mismatched clearAttributeName for " + name);
        }
        current.name = null;
        path.setLength(current.startPos);
    }

    /**
     * push an index onto the stack.
     *
     * This must be pushed on to a 'name', as there is no way to index anything
     * else.
     */
    public void setAttributeIndex(int index) {
        if (current.name == null) {
            throw new AuraRuntimeException("no name when index set");
        }
        if (current.index != -1) {
            throw new AuraRuntimeException("missing clearAttributeIndex");
        }
        current.index = index;
        path.append("[");
        path.append(index);
        path.append("]");
    }

    /**
     * pop a previously pushed index off the stack.
     */
    public void clearAttributeIndex(int index) {
        if (current.index != index) {
            throw new AuraRuntimeException("mismatched clearAttributeIndex");
        }
        current.index = -1;
        path.setLength(current.namePos);
    }

    /**
     * get the current path.
     */
    public String getPath() {
        return path.toString();
    }

    /**
     * get the top of the stack.
     */
    public Instance<?> peek() {
        int size = stack.size();
    return size > 0 ? stack.get(size - 1).instance : null;
    }

    /** Get the next 'id' for a component.
     *
     * This is actually only used for server side rendering, as these IDs are never
     * serialized to the client.
     */
    public int getNextId() {
        return nextId++;
    }

    /**
     * Register a component with the stack.
     *
     * This arguably does not belong here, but it does make some sense that it
     * is associated with the instance stack that has the paths for the components.
     *
     * WARNING: This function should not be directly called, it should only be used by
     * the context, as critical accounting happens there.
     *
     * @param component the component to register
     */
    public void registerComponent(BaseComponent<?, ?> component) {
        if (componentRegistry == null) {
            componentRegistry = Maps.newLinkedHashMap();
        }
        //
        // We should be able to assert that the component is not in our registry, but
        // defaults mess this up. I'm not sure that we even care, so for the moment, we
        // do nothing. Not sure if it is worth investigating adding stricter checks.
        //
        //
        componentRegistry.put(component.getPath(), component);
    }

    /**
     * Get the set of components.
     *
     * @return a map of all the components needed by the action.
     */
    public Map<String, BaseComponent<?, ?>> getComponents() {
        if (componentRegistry == null) {
            return Collections.emptyMap();
        }
        return componentRegistry;
    }

    /**
     * Is the stack currently in an unprivileged state?
     *
     * @return true if we have passed through an unprivileged namespace on the way here.
     */
    public boolean isUnprivileged() {
        return topUnprivileged != null;
    }

    /**
     * A private class to sort by creation path.
     */
    private static class CreationPathSorter implements Comparator<BaseComponent<?, ?>> {
        @Override
        public int compare(BaseComponent<?, ?> arg0, BaseComponent<?, ?> arg1) {
            return arg0.getPath().compareTo(arg1.getPath());
        }
    }

    private static final CreationPathSorter CP_SORTER = new CreationPathSorter();

    public void serializeAsPart(Json json) throws IOException {
        Map<String, BaseComponent<?, ?>> components = this.getComponents();

        if (components.isEmpty()) {
            //
            // If there is nothing here, we don't need to do anything.
            //
            return;
        }
        List<BaseComponent<?, ?>> sorted = Lists.newArrayList();

        for (BaseComponent<?,?> component : components.values()) {
            if (component.hasLocalDependencies()) {
                sorted.add(component);
            }
        }
        if (!sorted.isEmpty()) {
            Collections.sort(sorted, CP_SORTER);
            json.writeMapKey("components");
            json.writeArray(sorted);
        }
    }

    /**
     * Internal routine to get a stack info frame.
     */
    private void addStackInfo(Entry e, List<String> info) {
        StringBuffer sb = new StringBuffer();
        if (e.instance != null) {
            sb.append(e.instance.getDescriptor());
            if (e.name != null) {
                sb.append(".");
                sb.append(e.name);
                if (e.index != -1) {
                    sb.append("[");
                    sb.append(e.index);
                    sb.append("]");
                }
            }
        }
        if (sb.length() > 0) {
            info.add(sb.toString());
        }
    }

    /**
     * Get the current instance stack.
     */
    public List<String> getStackInfo() {
        List<String> info = Lists.newArrayList();

        if (current != null) {
            addStackInfo(current, info);
        }
        if (stack != null) {
            for (Entry e : stack) {
                addStackInfo(e, info);
            }
        }
        return info;
    }

    private static class Entry {
        public final Instance<?> instance;
        public final int startPos;
        public String name;
        public int namePos;
        public int count;
        public int index;
        public boolean top;
       
        public Entry(Instance<?> instance, int startPos) {
            this.instance = instance;
            this.startPos = startPos;
            this.namePos = -1;
            this.name = null;
            this.count = 0;
            this.top = false;
            this.index = -1;
        }
    };

    private Map<String, BaseComponent<?, ?>> componentRegistry = null;
    private int nextId = 1;
    private StringBuilder path;
    private List<Entry> stack;
    private Entry current;
    private final String base;
    private Instance<?> topUnprivileged;
}
TOP

Related Classes of org.auraframework.instance.InstanceStack$Entry

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.