/******************************************************************************* * 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.ui.workbench; import java.lang.ref.Reference; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPersistableElement; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.common.ResourceArray; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.exception.AdaptionException; import org.simantics.db.exception.DatabaseException; import org.simantics.db.service.LifecycleSupport; import org.simantics.ui.icons.ImageDescriptorProvider; import org.simantics.ui.workbench.editor.input.ResourceEditorInputMatchingStrategy; import org.simantics.utils.ObjectUtils; import org.simantics.utils.datastructures.cache.ProvisionException; import org.simantics.utils.ui.ErrorLogger; import org.simantics.utils.ui.workbench.StringMemento; /** * A basic editor input for Simantics database {@link Resource} instances. * * Editor extensions requiring these as input should always use * {@link ResourceEditorInputMatchingStrategy} as their matchingStrategy. * * @author Tuukka Lehtonen * * @see ResourceEditorInput2 * @see ResourceEditorInputMatchingStrategy */ public class ResourceEditorInput extends PlatformObject implements IResourceEditorInput, IPersistableElement { static final String NO_NAME = "(no name)"; private final String editorID; private List resourceIds; private transient Reference resources; private transient boolean exists; private transient String name; private transient String tooltip; private transient ImageDescriptor imageDesc; /** Persistent memento for external data */ private final StringMemento persistentStore = new StringMemento(); /** * @param editorID * @param r */ public ResourceEditorInput(String editorID, Resource r) { this(editorID, new ResourceArray(r)); } /** * @param editorID * @param ra */ public ResourceEditorInput(String editorID, ResourceArray ra) { if (editorID == null) throw new IllegalArgumentException("null editor id"); if (ra == null) throw new IllegalArgumentException("null resource array"); if (ra.isEmpty()) throw new IllegalArgumentException("input resource array is empty, expected non-empty list"); for (Resource r : ra.resources) if (r == null) throw new IllegalArgumentException("input resource array contains null resources: " + ra); this.editorID = editorID; this.resources = ResourceInputs.makeReference(ra); this.resourceIds = ResourceInputs.getRandomAccessIds(ra); setNonExistant(); } /** * @param editorID * @param randomAccessResourceId */ public ResourceEditorInput(String editorID, String randomAccessResourceId) { this(editorID, Collections.singletonList(randomAccessResourceId)); } /** * @param editorID * @param randomAccessResourceId a non-empty list of random access resource * ids * @throws IllegalArgumentException if the specified random access id list * is null or empty */ public ResourceEditorInput(String editorID, List randomAccessResourceId) { if (randomAccessResourceId == null) throw new IllegalArgumentException("null resource id list"); if (randomAccessResourceId.isEmpty()) throw new IllegalArgumentException("input resource id list is empty, expected non-empty list"); for (String id : randomAccessResourceId) if (id == null) throw new IllegalArgumentException("input resource id list contains null IDs: " + randomAccessResourceId); this.editorID = editorID; if (editorID == null) editorID = ""; this.resourceIds = Collections.unmodifiableList(new ArrayList(randomAccessResourceId)); this.resources = ResourceInputs.makeReference(ResourceArray.EMPTY); setNonExistant(); } @Override public void init(IAdaptable adapter) throws DatabaseException { // Initialize resource array if at all possible ResourceArray ra = getResourceArray(); if (!ra.isEmpty()) updateCaches(true); } @Override public void dispose() { //System.out.println("dispose resource editor input: " + name); // NOTE: this has to be done since Eclipse will cache these IEditorInput // instances within EditorHistoryItem's that are stored in an EditorHistory // instance. They are held by strong reference which means that the session // cannot be collected if it is not nulled here. resources = null; } @Override public boolean exists() { return exists; } @Override public boolean exists(ReadGraph graph) throws DatabaseException { for (Resource r : getResourceArray().resources) if (!graph.hasStatement(r)) return false; return true; } @Override public Resource getResource() { ResourceArray ra = getResourceArray(); return ra.isEmpty() ? null : ra.resources[0]; } public ResourceArray getResourceArray0() throws DatabaseException { ResourceArray ra = tryGetResourceArray(); if (!ra.isEmpty()) return ra; Session s = ResourceInputs.peekSession(); if (s == null) return ResourceArray.EMPTY; ra = ResourceInputs.makeResourceArray( s, resourceIds ); this.resources = ResourceInputs.makeReference( ra ); return ra; } @Override public ResourceArray getResourceArray() { try { return getResourceArray0(); } catch (DatabaseException e) { ErrorLogger.defaultLogError(e); return ResourceArray.EMPTY; } } /* (non-Javadoc) * @see org.eclipse.ui.IEditorInput#getImageDescriptor() */ @Override public ImageDescriptor getImageDescriptor() { return imageDesc; } /* (non-Javadoc) * @see org.eclipse.ui.IEditorInput#getName() */ @Override public String getName() { return name; } /* (non-Javadoc) * @see org.eclipse.ui.IEditorInput#getToolTipText() */ @Override public String getToolTipText() { return tooltip; } /* (non-Javadoc) * @see org.eclipse.ui.IEditorInput#getPersistable() */ @Override public IPersistableElement getPersistable() { // Don't allow persistability when it's not possible. if (!isPersistable()) return null; return this; } protected boolean isPersistable() { Session session = Simantics.peekSession(); if (session == null) return false; LifecycleSupport lc = session.peekService(LifecycleSupport.class); if (lc == null) return false; if (lc.isClosed()) return false; return true; } /* (non-Javadoc) * @see org.eclipse.ui.IPersistableElement#getFactoryId() */ @Override public String getFactoryId() { return ResourceEditorInputFactory.getFactoryId(); } /** * Saves the state of the given resource editor input into the given memento. * * @param memento the storage area for element state * @see org.eclipse.ui.IPersistable#saveState(org.eclipse.ui.IMemento) */ @Override public void saveState(IMemento memento) { for (String id : resourceIds) { IMemento child = memento.createChild(ResourceEditorInputFactory.TAG_RESOURCE_ID); child.putTextData(id); } memento.putString(ResourceEditorInputFactory.TAG_EDITOR_ID, editorID); memento.putString(ResourceEditorInputFactory.TAG_EXTERNAL_MEMENTO_ID, persistentStore.toString()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + editorID.hashCode(); result = prime * result + ObjectUtils.hashCode(resourceIds); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final ResourceEditorInput other = (ResourceEditorInput) obj; if (!editorID.equals(other.editorID)) return false; if (!ObjectUtils.objectEquals(resourceIds, other.resourceIds)) return false; return true; } private void updateCaches(boolean sync) throws DatabaseException { ReadRequest req = new ReadRequest() { @Override public void run(ReadGraph g) throws DatabaseException { update(g); } }; Session s = ResourceInputs.getSession(); if (sync) { s.syncRequest(req); } else { s.asyncRequest(req); } } /* (non-Javadoc) * @see org.simantics.ui.workbench.IResourceEditorInput#update(org.simantics.db.Graph) */ @Override public void update(ReadGraph g) throws DatabaseException { Resource r = getResource(); if (r == null) return; exists = g.hasStatement(r); if (exists) { name = g.syncRequest(new TitleRequest(editorID, this)); if (name == null) name = NO_NAME; tooltip = g.syncRequest(new ToolTipRequest(editorID, this)); if (tooltip == null) tooltip = NO_NAME; try { ImageDescriptorProvider idp = g.adapt(r, ImageDescriptorProvider.class); imageDesc = idp.get(); } catch (AdaptionException e) { imageDesc = ImageDescriptor.getMissingImageDescriptor(); } catch (ProvisionException e) { imageDesc = ImageDescriptor.getMissingImageDescriptor(); ErrorLogger.defaultLogError(e); } } else { setNonExistant(); } } private void setNonExistant() { exists = false; tooltip = name = NO_NAME; imageDesc = ImageDescriptor.getMissingImageDescriptor(); } public IMemento getPersistentStore() { return persistentStore; } @Override public String toString() { return getClass().getSimpleName() + " [name=" + getName() + ", resourceIds=" + resourceIds + ", resources=" + resources + "]"; } private ResourceArray tryGetResourceArray() { Reference ref = resources; if (ref == null) return ResourceArray.EMPTY; ResourceArray ra = ref.get(); return ra == null ? ResourceArray.EMPTY : ra; } }