/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.browsing.ui.common; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IAdaptable; import org.simantics.browsing.ui.BuiltinKeys; import org.simantics.browsing.ui.NodeContext; import org.simantics.browsing.ui.NodeContext.ConstantKey; import org.simantics.browsing.ui.content.Viewpoint; import org.simantics.utils.datastructures.ArrayMap; /** * Use this class in {@link Viewpoint}, and particularly * {@link Viewpoint#getChildren()} implementations to construct new INodeContext * instances. * * @author Antti Villberg * @author Tuukka Lehtonen */ public final class NodeContextBuilder { private static Set> SINGLE_INPUT_KEYS = Collections.> singleton(BuiltinKeys.INPUT); MapNodeContext context; private static class InputNodeContext implements NodeContext { final Object input; private InputNodeContext(Object input) { this.input = input; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Object getAdapter(Class adapter) { if (NodeContext.class.equals(adapter)) return this; if (input != null) { if (adapter.isAssignableFrom(input.getClass())) return input; if (input instanceof IAdaptable) return ((IAdaptable) input).getAdapter(adapter); } return null; } @SuppressWarnings("unchecked") @Override public T getConstant(ConstantKey key) { return key == BuiltinKeys.INPUT ? (T) input : null; } @Override public Set> getKeys() { return SINGLE_INPUT_KEYS; } @Override public int hashCode() { return ((input == null) ? 0 : input.hashCode()); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; InputNodeContext other = (InputNodeContext) obj; if (input == null) { if (other.input != null) return false; } else if (!input.equals(other.input)) return false; return true; } @Override public String toString() { return "InputNodeContext(" + hashCode() + ") [" + input + "]"; } } public static class MapNodeContext implements NodeContext { private Map, Object> data; private int hash; private MapNodeContext() { } void makeHash() { this.hash = (data == null) ? 0 : data.hashCode(); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public Object getAdapter(Class adapter) { if (NodeContext.class.equals(adapter)) return this; Object input = data.get(BuiltinKeys.INPUT); if (input != null) { if (adapter.isAssignableFrom(input.getClass())) return input; if (input instanceof IAdaptable) return ((IAdaptable) input).getAdapter(adapter); } return null; } @SuppressWarnings("unchecked") @Override public T getConstant(ConstantKey key) { return data != null ? (T) data.get(key) : null; } @Override public Set> getKeys() { return data.keySet(); } @Override public int hashCode() { return hash; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MapNodeContext other = (MapNodeContext) obj; if (data == null) { if (other.data != null) return false; } else if (!data.equals(other.data)) return false; return true; } @Override public String toString() { return "MapNodeContext(" + hashCode() + ") [" + (data != null ? data : "") + "]"; } } public NodeContextBuilder() { this.context = new MapNodeContext(); } public NodeContextBuilder define(ConstantKey key, T value) { if (context.data == null) context.data = new HashMap, Object>(); context.data.put(key, value); return this; } public NodeContext createContext() { context.makeHash(); return context; } /** * A special case builder for an INodeContext that will only take one * (key,value) pair and insert that into the built INodeContext. * *

* This method works around the need to create NodeContextBuilder instances. *

* *

* Notice that your input key and value must properly implement * equals and hashCode in order for * GraphExplorer's automatic node expansion state tracking to work. *

* * @param key * @param value * @return an INodeContext containing a single constant as specified by * key and value */ public static NodeContext buildWithSingleData(ConstantKey key, T value) { MapNodeContext context = new MapNodeContext(); context.data = Collections., Object>singletonMap(key, value); context.makeHash(); return context; } /** * A generic case builder for an INodeContext that will take as many * (key,value) pairs as it is given and insert all of them into a map * that is backing the built INodeContext implementation. * *

* This method works around the need to create NodeContextBuilder instances. *

* *

* Notice that your input keys and values must properly implement * equals and hashCode in order for * GraphExplorer's automatic node expansion state tracking to work. *

* * @param keyValuePairs * @return an INodeContext containing the specified key,value pairs as its * constants */ public static NodeContext buildWithData(Object... keyValuePairs) { assert keyValuePairs.length % 2 == 0; MapNodeContext context = new MapNodeContext(); int size = keyValuePairs.length; Map, Object> map = new HashMap, Object>(size); for (int i = 0; i < keyValuePairs.length; i += 2) { map.put((ConstantKey) keyValuePairs[i], keyValuePairs[i+1]); } context.data = map; context.makeHash(); return context; } /** * A generic case builder for an INodeContext that will take as many * (key,value) pairs as it is given and insert all of them into a map that * is backing the built INodeContext implementation. This method takes the * keys and values separately, which allows the use of {@link ArrayMap} that * provides a space-optimized Map implementation. Note that the key set of * an {@link ArrayMap} is immutable. * *

* This method works around the need to create NodeContextBuilder instances. *

* *

* Notice that your input keys and values must properly implement * equals and hashCode in order for * GraphExplorer's automatic node expansion state tracking to work. *

* * @param keys the keys of the map, must equal values array in size * @param values the values of the map, must equal keys array in size * @return a NodeContext containing the specified key,value * pairs as its constants */ public static NodeContext buildWithData(ConstantKey[] keys, Object[] values) { assert keys.length == values.length; MapNodeContext context = new MapNodeContext(); Map, Object> map = new ArrayMap, Object>(keys, values); context.data = map; context.makeHash(); return context; } /** * A special case builder for an INodeContext that will only take one * (key,value) pair and insert that into the built INodeContext. * *

* This method works around the need to create NodeContextBuilder instances. *

* *

* Notice that your input object must properly implement equals * and hashCode in order for GraphExplorer's automatic node * expansion state tracking to work. *

* * @param input * @return an INodeContext containing the specified object as with the key * {@link BuiltinKeys#INPUT} */ public static NodeContext buildWithInput(Object input) { return new InputNodeContext(input); } }