/******************************************************************************* * Copyright 2013 Geoscience Australia * * 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 au.gov.ga.earthsci.editable; import java.lang.reflect.Field; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.RegistryFactory; import org.eclipse.sapphire.Element; import org.eclipse.sapphire.ui.def.DefinitionLoader; import org.eclipse.sapphire.ui.forms.PropertyEditorPart; import org.eclipse.sapphire.ui.forms.swt.PropertyEditorPresentationFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import au.gov.ga.earthsci.common.collection.ArrayListTreeMap; import au.gov.ga.earthsci.common.collection.ListSortedMap; /** * Manages the list of {@link EditableElement}s, defined via the * <code>au.gov.ga.earthsci.editable.elements</code> extension point. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class EditableManager { private static final String ELEMENTS_ID = "au.gov.ga.earthsci.editable.elements"; //$NON-NLS-1$ private static final String RENDERERS_ID = "au.gov.ga.earthsci.editable.renderers"; //$NON-NLS-1$ private static final Logger logger = LoggerFactory.getLogger(EditableManager.class); private static EditableManager instance = new EditableManager(); /** * @return An instance of the intent manager */ public static EditableManager getInstance() { return instance; } private final Map<Class<?>, EditableElement<?>> typeElements = new HashMap<Class<?>, EditableElement<?>>(); private EditableManager() { IConfigurationElement[] elements = RegistryFactory.getRegistry().getConfigurationElementsFor(ELEMENTS_ID); for (IConfigurationElement element : elements) { try { EditableElement<?> typeElement = new EditableElement<Object>(element); this.typeElements.put(typeElement.getType(), typeElement); } catch (Exception e) { logger.error("Error processing editable element", e); //$NON-NLS-1$ } } ListSortedMap<Integer, PropertyEditorPresentationFactory> factories = new ArrayListTreeMap<Integer, PropertyEditorPresentationFactory>(); elements = RegistryFactory.getRegistry().getConfigurationElementsFor(RENDERERS_ID); for (IConfigurationElement element : elements) { try { PropertyEditorPresentationFactory factory = (PropertyEditorPresentationFactory) element.createExecutableExtension("factory"); //$NON-NLS-1$ String stringPriority = element.getAttribute("priority"); //$NON-NLS-1$ int priority = stringPriority == null || stringPriority.length() == 0 ? 0 : Integer.parseInt(stringPriority); factories.putSingle(priority, factory); } catch (Exception e) { logger.error("Error processing renderer factory", e); //$NON-NLS-1$ } } try { Field FACTORIES_field = PropertyEditorPart.class.getDeclaredField("FACTORIES"); //$NON-NLS-1$ FACTORIES_field.setAccessible(true); @SuppressWarnings({ "unchecked", "rawtypes" }) List<PropertyEditorPresentationFactory> list = (List) FACTORIES_field.get(null); for (Entry<Integer, List<PropertyEditorPresentationFactory>> entry : factories.entrySet()) { for (PropertyEditorPresentationFactory factory : entry.getValue()) { //add factories before all default ones, inserting them at the front so that highest priority is at index 0 list.add(0, factory); } } } catch (Exception e) { logger.error("Error adding factory", e); //$NON-NLS-1$ } } public <E> Element createElement(E object) { EditableElement<? super E> editable = getEditable(object); if (editable == null) { return null; } return ElementFactory.createElement(object, editable); } public <E> ElementAndDefinition edit(E object) { EditableElement<? super E> editable = getEditable(object); if (editable == null) { return null; } Element element = createElement(object); DefinitionLoader loader = DefinitionLoader.context(editable.getSdefContext()).sdef(editable.getSdefName()); return new ElementAndDefinition(element, loader); } public <E> EditableElement<? super E> getEditable(E element) { Map<EditableElement<?>, Integer> distances = new HashMap<EditableElement<?>, Integer>(); calculateEditableDistances(element.getClass(), 0, distances); int minDistance = Integer.MAX_VALUE; EditableElement<? super E> editable = null; for (Entry<EditableElement<?>, Integer> entry : distances.entrySet()) { if (entry.getValue() < minDistance) { minDistance = entry.getValue(); @SuppressWarnings({ "rawtypes", "unchecked" }) EditableElement<? super E> e = (EditableElement) entry.getKey(); editable = e; } } return editable; } private void calculateEditableDistances(Class<?> c, int distance, Map<EditableElement<?>, Integer> distances) { if (c == null) { return; } EditableElement<?> editable = typeElements.get(c); if (editable != null) { Integer lastDistance = distances.get(editable); if (lastDistance == null || distance < lastDistance) { distances.put(editable, distance); } } calculateEditableDistances(c.getSuperclass(), distance + 1, distances); for (Class<?> i : c.getInterfaces()) { calculateEditableDistances(i, distance + 1, distances); } } }