Package org.thechiselgroup.choosel.core.client.visualization.model.implementation

Source Code of org.thechiselgroup.choosel.core.client.visualization.model.implementation.DefaultVisualItem

/*******************************************************************************
* Copyright 2009, 2010 Lars Grammel
*
* 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.thechiselgroup.choosel.core.client.visualization.model.implementation;

import java.util.EnumMap;
import java.util.Map;

import org.thechiselgroup.choosel.core.client.resources.DefaultResourceSet;
import org.thechiselgroup.choosel.core.client.resources.Resource;
import org.thechiselgroup.choosel.core.client.resources.ResourceSet;
import org.thechiselgroup.choosel.core.client.resources.ResourceSetChangedEvent;
import org.thechiselgroup.choosel.core.client.resources.ResourceSetChangedEventHandler;
import org.thechiselgroup.choosel.core.client.util.Disposable;
import org.thechiselgroup.choosel.core.client.util.collections.CollectionFactory;
import org.thechiselgroup.choosel.core.client.util.collections.LightweightCollection;
import org.thechiselgroup.choosel.core.client.util.event.EventHandlerPriority;
import org.thechiselgroup.choosel.core.client.util.event.PrioritizedEventHandler;
import org.thechiselgroup.choosel.core.client.visualization.model.NoResolverForSlotException;
import org.thechiselgroup.choosel.core.client.visualization.model.Slot;
import org.thechiselgroup.choosel.core.client.visualization.model.SlotMappingResolutionException;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItem;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItemInteraction;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItemInteractionHandler;
import org.thechiselgroup.choosel.core.client.visualization.model.VisualItemValueResolverContext;

/**
* Default implementation of {@link VisualItem}.
* <p>
* <b>PERFORMANCE NOTE</b>: Provides caching for calculated slot values and for
* highlighting and selection status.
* </p>
*
* @author Lars Grammel
*/
public class DefaultVisualItem implements Disposable, VisualItem {

    private final class CacheUpdateOnResourceSetChange implements
            ResourceSetChangedEventHandler, PrioritizedEventHandler {

        @Override
        public EventHandlerPriority getPriority() {
            return EventHandlerPriority.FIRST;
        }

        @Override
        public void onResourceSetChanged(ResourceSetChangedEvent event) {
            valueCache.clear();
        }
    }

    private final class SubsetContainer {

        private ResourceSet resources = new DefaultResourceSet();

        private Status cachedStatus;

        private Status calculateSubsetStatus() {
            if (resources.isEmpty()) {
                return Status.NONE;
            }

            if (resources.containsEqualResources(content)) {
                return Status.FULL;
            }

            return Status.PARTIAL;
        }

        public void clearStatusCache() {
            cachedStatus = null;
        }

        public Status getStatus() {
            if (cachedStatus == null) {
                cachedStatus = calculateSubsetStatus();
            }

            return cachedStatus;
        }

        public void update(
                LightweightCollection<Resource> addedSubsetResources,
                LightweightCollection<Resource> removedSubsetResources) {

            clearStatusCache();
            resources.addAll(content.getIntersection(addedSubsetResources));
            resources
                    .removeAll(content.getIntersection(removedSubsetResources));
        }

    }

    private String id;

    // TODO update & paint on changes in resources!!!
    private final ResourceSet content;

    private final VisualItemValueResolverContext valueResolverContext;

    /**
     * The representation of this resource item in the specific display. This is
     * set by the display to enable fast reference to this display element, and
     * should be casted into the specific type.
     */
    // TODO dispose
    private Object displayObject;

    private Map<Subset, SubsetContainer> subsets = new EnumMap<Subset, SubsetContainer>(
            Subset.class);

    /**
     * PERFORMANCE: Cache for the resolved slot values of ALL subset. Maps the
     * slot id to the value.
     *
     * @see #clearValueCache(Slot)
     */
    private Map<String, Object> valueCache = CollectionFactory
            .createStringMap();

    private final VisualItemInteractionHandler interactionHandler;

    public DefaultVisualItem(String id, ResourceSet content,
            VisualItemValueResolverContext valueResolverContext,
            VisualItemInteractionHandler interactionHandler) {

        assert id != null;
        assert content != null;
        assert valueResolverContext != null;
        assert interactionHandler != null;

        this.id = id;
        this.content = content;
        this.valueResolverContext = valueResolverContext;
        this.interactionHandler = interactionHandler;

        this.subsets.put(Subset.SELECTED, new SubsetContainer());
        this.subsets.put(Subset.HIGHLIGHTED, new SubsetContainer());

        this.content.addEventHandler(new CacheUpdateOnResourceSetChange());
        this.content.addEventHandler(new ResourceSetChangedEventHandler() {
            @Override
            public void onResourceSetChanged(ResourceSetChangedEvent event) {
                LightweightCollection<Resource> removedResources = event
                        .getRemovedResources();

                getResources(Subset.HIGHLIGHTED).removeAll(removedResources);
                getResources(Subset.SELECTED).removeAll(removedResources);
            }
        });
    }

    /**
     * <p>
     * Clears the value cached for {@code slot}.
     * </p>
     * <p>
     * We have chosen to provide an explicit clear method that is called by
     * {@link DefaultVisualizationModel} instead of listening for slot mapping
     * changes, because there are several constraints on the execution order
     * (e.g. the cache needs to be cleared before the view is re-rendered) and
     * those constraints are easier to develop, test and understand when they
     * are specified in a straight-forward algorithm.
     * </p>
     */
    public void clearValueCache(Slot slot) {
        valueCache.remove(slot.getId());
    }

    @Override
    public void dispose() {
        // XXX deregister event listeners
        // XXX dispose intersection sets

    }

    @Override
    public Object getDisplayObject() {
        return displayObject;
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public ResourceSet getResources() {
        return content;
    }

    @Override
    public ResourceSet getResources(Subset subset) {
        assert subset != null;

        switch (subset) {
        case ALL:
            return getResources();
        case HIGHLIGHTED:
        case SELECTED:
            return getSubsetContainer(subset).resources;
        }

        throw new RuntimeException("invalid subset: " + subset);
    }

    // TODO move, refactor
    @Override
    public Slot[] getSlots() {
        return valueResolverContext.getSlots();
    }

    @Override
    public Status getStatus(Subset subset) {
        assert subset != null;

        switch (subset) {
        case ALL:
            // always containing all contained resources
            return Status.FULL;
        case HIGHLIGHTED:
        case SELECTED:
            return getSubsetContainer(subset).getStatus();
        }

        throw new RuntimeException("invalid subset: " + subset);
    }

    private SubsetContainer getSubsetContainer(Subset subset) {
        return subsets.get(subset);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getValue(Slot slot) {
        assert slot != null : "slot must not be null";

        String slotId = slot.getId();
        if (valueCache.containsKey(slotId)) {
            return (T) valueCache.get(slotId);
        }

        try {
            Object value = valueResolverContext.getResolver(slot).resolve(this,
                    valueResolverContext);

            valueCache.put(slotId, value);

            return (T) value;
        } catch (NoResolverForSlotException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new SlotMappingResolutionException(slot, this, ex);
        }
    }

    @Override
    public double getValueAsDouble(Slot slot) {
        return this.<Number> getValue(slot).doubleValue();
    }

    @Override
    public boolean isStatus(Subset subset, Status... status) {
        Status realStatus = getStatus(subset);
        for (Status expectedStatus : status) {
            if (realStatus.equals(expectedStatus)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void reportInteraction(VisualItemInteraction interaction) {
        assert interaction != null;
        interactionHandler.onInteraction(this, interaction);
    }

    @Override
    public void setDisplayObject(Object displayObject) {
        this.displayObject = displayObject;
    }

    @Override
    public String toString() {
        return "VisualItem[" + content.toString() + "]";
    }

    public void updateSubset(Subset subset,
            LightweightCollection<Resource> addedSubsetResources,
            LightweightCollection<Resource> removedSubsetResources) {

        assert addedSubsetResources != null;
        assert removedSubsetResources != null;

        valueCache.clear();
        getSubsetContainer(subset).update(addedSubsetResources,
                removedSubsetResources);
    }

}
TOP

Related Classes of org.thechiselgroup.choosel.core.client.visualization.model.implementation.DefaultVisualItem

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.