/** * Page for editing template information. * * @author Simon Templer */ @PageDescription(title = "Edit Template") public class EditTemplatePage extends SecuredPage { private static final long serialVersionUID = 5042784175750862684L; private static final ALogger log = ALoggerFactory.getLogger(EditTemplatePage.class); /** @see SecuredPage#SecuredPage(PageParameters) */ public EditTemplatePage(PageParameters parameters) { super(parameters); } @Override protected void addControls() { StringValue idParam = getPageParameters().get(0); if (!idParam.isNull() && !idParam.isEmpty()) { String templateId = idParam.toString(); OrientGraph graph = DatabaseHelper.getGraph(); try { Template template = null; try { template = Template.getByTemplateId(graph, templateId); } catch (NonUniqueResultException e) { log.error("Duplicate template representation: " + templateId, e); } if (template != null) { // get associated user Vertex v = template.getV(); Iterator<Vertex> owners = v.getVertices(Direction.OUT, "owner").iterator(); if (UserUtil.isAdmin() // user is admin // or user is owner || (owners.hasNext() && UserUtil.getLogin().equals(new User(owners.next(), graph).getLogin()))) { add(new TemplateForm("edit-form", false, templateId)); } else { throw new AbortWithHttpErrorCodeException(HttpServletResponse.SC_FORBIDDEN); } } else { throw new AbortWithHttpErrorCodeException( HttpServletResponse.SC_NOT_FOUND, "Template not found."); } } finally { graph.shutdown(); } } else throw new AbortWithHttpErrorCodeException( HttpServletResponse.SC_BAD_REQUEST, "Template identifier must be specified."); } }
/** * Extension for cell annotations. * * @author Simon Templer */ public class AnnotationExtension extends IdentifiableExtension<DefinedAnnotation> { private static final ALogger log = ALoggerFactory.getLogger(AnnotationExtension.class); /** The extension identifier. */ public static final String EXTENSION_ID = "eu.esdihumboldt.hale.align.annotation"; private static AnnotationExtension instance; /** * Get the extension instance. * * @return the extension singleton */ public static AnnotationExtension getInstance() { synchronized (AnnotationExtension.class) { if (instance == null) instance = new AnnotationExtension(); } return instance; } /** Default constructor */ protected AnnotationExtension() { super(EXTENSION_ID); } @SuppressWarnings("unchecked") @Override protected DefinedAnnotation create(String id, IConfigurationElement conf) { try { return new DefinedAnnotation( id, (Class<AnnotationDescriptor<?>>) ExtensionUtil.loadClass(conf, "descriptor")); } catch (Exception e) { log.error("Could not load annotation descriptor for type identifier " + id, e); return null; } } @Override protected String getIdAttributeName() { return "type"; } }
/** * Default alignment implementation. * * @author Simon Templer */ public class DefaultAlignment implements Alignment, MutableAlignment { private static final ALogger log = ALoggerFactory.getLogger(DefaultAlignment.class); /** List with all cells contained in the alignment. XXX use a LinkedHashSet instead? */ private final Collection<Cell> cells = new ArrayList<Cell>(); /** List with all type cells contained in the alignment. */ private final Collection<Cell> typeCells = new ArrayList<Cell>(); /** Entity definitions mapped to alignment cells. */ private final ListMultimap<EntityDefinition, Cell> cellsPerEntity = ArrayListMultimap.create(); /** Source types mapped to alignment cells. */ private final ListMultimap<TypeDefinition, Cell> cellsPerSourceType = ArrayListMultimap.create(); /** Target types mapped to alignment cells. */ private final ListMultimap<TypeDefinition, Cell> cellsPerTargetType = ArrayListMultimap.create(); private final Map<String, URI> baseAlignments = new HashMap<String, URI>(); private final Map<String, Cell> idToCell = new HashMap<String, Cell>(); /** Custom functions defined in this alignment. */ private final Map<String, CustomPropertyFunction> idToPropertyFunction = new HashMap<>(); /** Custom functions defined in base alignments. */ private final Map<String, CustomPropertyFunction> idToBaseFunction = new HashMap<>(); /** Default constructor. */ public DefaultAlignment() { // do nothing } /** * Copy constructor. Adds all cells of the given alignment. * * @param alignment the alignment to copy */ public DefaultAlignment(Alignment alignment) { // Since the cells came out of another alignment just pass addCell for (Cell cell : alignment.getCells()) { /* * But copy the cell as it is not immutable (things like * transformation mode and priority may change) */ internalAdd(new DefaultCell(cell)); // XXX is this working properly // for BaseAlignmentCells? } baseAlignments.putAll(alignment.getBaseAlignments()); idToPropertyFunction.putAll(alignment.getCustomPropertyFunctions()); idToBaseFunction.putAll(alignment.getBasePropertyFunctions()); } @Override public Map<String, CustomPropertyFunction> getBasePropertyFunctions() { return Collections.unmodifiableMap(idToBaseFunction); } @Override public Map<String, CustomPropertyFunction> getCustomPropertyFunctions() { return Collections.unmodifiableMap(idToPropertyFunction); } @Override public Map<String, CustomPropertyFunction> getAllCustomPropertyFunctions() { Map<String, CustomPropertyFunction> result = new HashMap<>(idToBaseFunction); result.putAll(idToPropertyFunction); return Collections.unmodifiableMap(result); } @Override public void addCustomPropertyFunction(CustomPropertyFunction function) { // FIXME prevent overriding base alignment functions? idToPropertyFunction.put(function.getDescriptor().getId(), function); } @Override public boolean removeCustomPropertyFunction(String id) { return idToPropertyFunction.remove(id) != null; } /** @see MutableAlignment#addCell(MutableCell) */ @Override public void addCell(MutableCell cell) { if (cell.getId() == null) { // the cell has to be a newly created cell String id; do { id = "C" + UUID.randomUUID().toString(); } while (idToCell.containsKey(id)); cell.setId(id); } internalAdd(cell); } /** * Add a cell to the various internal containers. * * @param cell the cell to add */ private void internalAdd(Cell cell) { cells.add(cell); idToCell.put(cell.getId(), cell); // check if cell is a type cell if (AlignmentUtil.isTypeCell(cell)) { typeCells.add(cell); } // add to maps internalAddToMaps(cell.getSource(), cell); internalAddToMaps(cell.getTarget(), cell); } /** * Add a cell to the internal indexes, based on the given associated entities. * * @param entities the cell entities (usually either source or target) * @param cell the cell to add */ private void internalAddToMaps(ListMultimap<String, ? extends Entity> entities, Cell cell) { if (entities == null) { return; } for (Entity entity : entities.values()) { EntityDefinition entityDef = entity.getDefinition(); cellsPerEntity.put(entityDef, cell); switch (entityDef.getSchemaSpace()) { case TARGET: cellsPerTargetType.put(entityDef.getType(), cell); break; case SOURCE: cellsPerSourceType.put(entityDef.getType(), cell); break; default: throw new IllegalStateException( "Entity definition with illegal schema space encountered"); } } } /** @see Alignment#getCells(EntityDefinition) */ @Override public Collection<? extends Cell> getCells(EntityDefinition entityDefinition) { return getCells(entityDefinition, false); } /** @see Alignment#getCells(EntityDefinition, boolean) */ @Override public Collection<? extends Cell> getCells( EntityDefinition entityDefinition, boolean includeInherited) { if (!includeInherited) return Collections.unmodifiableCollection(cellsPerEntity.get(entityDefinition)); else { // Set for safety to return each cell only once. // Duplicates shouldn't happen in usual cases, though. Collection<Cell> cells = new HashSet<Cell>(); EntityDefinition e = entityDefinition; do { cells.addAll(cellsPerEntity.get(e)); if (e.getFilter() != null) { cells.addAll( cellsPerEntity.get( AlignmentUtil.createEntity( e.getType(), e.getPropertyPath(), e.getSchemaSpace(), null))); } TypeDefinition superType = e.getType().getSuperType(); e = superType == null ? null : AlignmentUtil.createEntity( superType, e.getPropertyPath(), e.getSchemaSpace(), e.getFilter()); } while (e != null); return cells; } } /** @see Alignment#getCells(TypeDefinition, SchemaSpaceID) */ @Override public Collection<? extends Cell> getCells(TypeDefinition type, SchemaSpaceID schemaSpace) { switch (schemaSpace) { case SOURCE: return Collections.unmodifiableCollection(cellsPerSourceType.get(type)); case TARGET: return Collections.unmodifiableCollection(cellsPerTargetType.get(type)); default: throw new IllegalArgumentException("Illegal schema space provided"); } } /** @see Alignment#getPropertyCells(Cell) */ @Override public Collection<? extends Cell> getPropertyCells(Cell typeCell) { return getPropertyCells(typeCell, false, false); } /** @see Alignment#getPropertyCells(Cell, boolean, boolean) */ @Override public Collection<? extends Cell> getPropertyCells( Cell typeCell, boolean includeDisabled, boolean ignoreEmptySource) { if (CellUtil.getFirstEntity(typeCell.getTarget()) == null) { if (CellUtil.getFirstEntity(typeCell.getSource()) == null) return Collections.emptySet(); else if (!(typeCell.getSource().values().iterator().next() instanceof Type)) throw new IllegalArgumentException("Given cell is not a type cell."); // query with sources only Collection<TypeEntityDefinition> sourceTypes = new ArrayList<TypeEntityDefinition>(); Iterator<? extends Entity> it = typeCell.getSource().values().iterator(); while (it.hasNext()) sourceTypes.add((TypeEntityDefinition) it.next().getDefinition()); List<Cell> result = new ArrayList<Cell>(); for (Cell cell : cells) if (!AlignmentUtil.isTypeCell(cell) && matchesSources(cell.getSource(), sourceTypes)) result.add(cell); return result; } if (!AlignmentUtil.isTypeCell(typeCell)) throw new IllegalArgumentException("Given cell is not a type cell."); List<Cell> result = new ArrayList<Cell>(); TypeDefinition typeCellType = typeCell.getTarget().values().iterator().next().getDefinition().getType(); // collect source entity definitions Collection<TypeEntityDefinition> sourceTypes = new ArrayList<TypeEntityDefinition>(); // null check in case only target type is in question if (typeCell.getSource() != null) { Iterator<? extends Entity> it = typeCell.getSource().values().iterator(); while (it.hasNext()) sourceTypes.add((TypeEntityDefinition) it.next().getDefinition()); } while (typeCellType != null) { // select all cells of the target type for (Cell cell : cellsPerTargetType.get(typeCellType)) { // check all cells associated to the target type if (!AlignmentUtil.isTypeCell(cell) && (includeDisabled || !cell.getDisabledFor().contains(typeCell.getId()))) { // cell is a property cell that isn't disabled // the target type matches, too if (AlignmentUtil.isAugmentation(cell) || (ignoreEmptySource && sourceTypes.isEmpty()) || matchesSources(cell.getSource(), sourceTypes)) { // cell matches on the source side, too result.add(cell); } } } // continue with super type for inheritance typeCellType = typeCellType.getSuperType(); } return result; } /** * Determines if the given type entity definition of the test cell is associated to at least one * of the given type entity definitions of a type cell. * * @param testCellType type entity definition of the test cell * @param typeCellTypes type entity definitions of a type cell * @return whether the entity definition is associated to at least one of the others */ private boolean matchesSources( TypeEntityDefinition testCellType, Iterable<TypeEntityDefinition> typeCellTypes) { TypeDefinition def = testCellType.getDefinition(); Filter filter = testCellType.getFilter(); for (TypeEntityDefinition typeCellType : typeCellTypes) { if (DefinitionUtil.isSuperType(typeCellType.getDefinition(), def) && (filter == null || filter.equals(typeCellType.getFilter()))) return true; } return false; } /** * Determines if all of the given entities are associated to at least one of the given type entity * definitions. * * @param testCellSources the entities * @param typeCellTypes the type entity definitions * @return whether all entities are associated to at least one of the types */ private boolean matchesSources( ListMultimap<String, ? extends Entity> testCellSources, Iterable<TypeEntityDefinition> typeCellTypes) { if (testCellSources == null) return true; for (Entity entity : testCellSources.values()) { // if one of the property cell's sources is not part of the type // cell it should not be included // XXX this is only true for the transformation? if (!matchesSources(AlignmentUtil.getTypeEntity(entity.getDefinition()), typeCellTypes)) return false; } return true; } /** @see Alignment#getTypeCells(Cell) */ @Override public Collection<? extends Cell> getTypeCells(Cell queryCell) { Set<TypeEntityDefinition> sources = new HashSet<TypeEntityDefinition>(); if (queryCell.getSource() != null) { Iterator<? extends Entity> it = queryCell.getSource().values().iterator(); while (it.hasNext()) sources.add(AlignmentUtil.getTypeEntity(it.next().getDefinition())); } Entity targetEntity = CellUtil.getFirstEntity(queryCell.getTarget()); TypeDefinition target = targetEntity == null ? null : targetEntity.getDefinition().getType(); if (sources.isEmpty() && target == null) return getTypeCells(); List<Cell> result = new ArrayList<Cell>(); for (Cell typeCell : typeCells) { TypeDefinition typeCellTarget = CellUtil.getFirstEntity(typeCell.getTarget()).getDefinition().getType(); if (target == null || DefinitionUtil.isSuperType(target, typeCellTarget)) { // target matches if (sources.isEmpty() || matchesSources(typeCell.getSource(), sources)) { // source matches, too result.add(typeCell); } } } return result; } /** @see Alignment#getCells() */ @Override public Collection<? extends Cell> getCells() { return Collections.unmodifiableCollection(cells); } /** @see MutableAlignment#removeCell(Cell) */ @Override public boolean removeCell(Cell cell) { boolean removed = cells.remove(cell); if (removed) { typeCells.remove(cell); idToCell.remove(cell.getId()); // remove from maps internalRemoveFromMaps(cell.getSource(), cell); internalRemoveFromMaps(cell.getTarget(), cell); } return removed; } /** * Removes a cell from the internal indexes, based on the given associated entities. * * @param entities the cell entities (usually either source or target) * @param cell the cell to remove */ private void internalRemoveFromMaps(ListMultimap<String, ? extends Entity> entities, Cell cell) { if (entities == null) { return; } for (Entity entity : entities.values()) { EntityDefinition entityDef = entity.getDefinition(); cellsPerEntity.remove(entityDef, cell); switch (entityDef.getSchemaSpace()) { case TARGET: cellsPerTargetType.remove(entityDef.getType(), cell); break; case SOURCE: cellsPerSourceType.remove(entityDef.getType(), cell); break; default: throw new IllegalStateException( "Entity definition with illegal schema space encountered"); } } } /** @see Alignment#getTypeCells() */ @Override public Collection<Cell> getTypeCells() { return Collections.unmodifiableCollection(typeCells); } @Override public Collection<? extends Cell> getActiveTypeCells() { List<Cell> result = new ArrayList<Cell>(); for (Cell cell : getTypeCells()) { if (cell.getTransformationMode() == TransformationMode.active) { result.add(cell); } } return result; } @Override public void addBaseAlignment( String prefix, URI alignment, Iterable<BaseAlignmentCell> cells, Iterable<CustomPropertyFunction> baseFunctions) { if (baseAlignments.containsValue(alignment)) throw new IllegalArgumentException("base alignment " + alignment + " already included"); if (baseAlignments.containsKey(prefix)) throw new IllegalArgumentException("prefix " + prefix + " already in use."); baseAlignments.put(prefix, alignment); for (BaseAlignmentCell cell : cells) { internalAdd(cell); } for (CustomPropertyFunction function : baseFunctions) { if (idToBaseFunction.put(function.getDescriptor().getId(), function) != null) { log.error( MessageFormat.format( "Function with identifier {0} defined multiple times in base alignments", function.getDescriptor().getId())); } } } @Override public Map<String, URI> getBaseAlignments() { return Collections.unmodifiableMap(baseAlignments); } @Override public Cell getCell(String cellId) { return idToCell.get(cellId); } @Override public Iterable<BaseAlignmentCell> getBaseAlignmentCells(URI baseAlignment) { // expect this operation to not be needed regularly and thus do not // optimize it Collection<BaseAlignmentCell> baseCells = new ArrayList<BaseAlignmentCell>(); for (Cell cell : cells) { if (cell instanceof BaseAlignmentCell) { BaseAlignmentCell bac = (BaseAlignmentCell) cell; if (bac.getBaseAlignment().equals(baseAlignment)) baseCells.add(bac); } } return baseCells; } @Override public void clearBaseAlignments() { // clear base function related information baseAlignments.clear(); idToBaseFunction.clear(); // clear any base alignment cells cells.removeIf(cell -> cell instanceof BaseAlignmentCell); cellsPerEntity.values().removeIf(cell -> cell instanceof BaseAlignmentCell); cellsPerSourceType.values().removeIf(cell -> cell instanceof BaseAlignmentCell); cellsPerTargetType.values().removeIf(cell -> cell instanceof BaseAlignmentCell); idToCell.values().removeIf(cell -> cell instanceof BaseAlignmentCell); typeCells.removeIf(cell -> cell instanceof BaseAlignmentCell); } @Override public void clearCells() { // clear all cells except base alignment cells cells.removeIf(cell -> !(cell instanceof BaseAlignmentCell)); cellsPerEntity.values().removeIf(cell -> !(cell instanceof BaseAlignmentCell)); cellsPerSourceType.values().removeIf(cell -> !(cell instanceof BaseAlignmentCell)); cellsPerTargetType.values().removeIf(cell -> !(cell instanceof BaseAlignmentCell)); idToCell.values().removeIf(cell -> !(cell instanceof BaseAlignmentCell)); typeCells.removeIf(cell -> !(cell instanceof BaseAlignmentCell)); } }
/** * This class provides a SWT Adapter for Swing Actions. * * @author Simon Templer, Thorsten Reitz * @partner 01 / Fraunhofer Institute for Computer Graphics Research */ public class RcpActionAdapter extends Action implements PropertyChangeListener { private static final ALogger _log = ALoggerFactory.getLogger(RcpActionAdapter.class); private final javax.swing.Action action; /** The display */ protected final Display display; /** * Creates an ActionAdapter * * @param action the Swing action to wrap */ public RcpActionAdapter(final javax.swing.Action action) { this(action, Action.AS_PUSH_BUTTON); } /** * Creates an ActionAdapter * * @param action the Swing action to wrap * @param style the JFace action style * @see Action#Action(String, int) */ public RcpActionAdapter(final javax.swing.Action action, int style) { super(null, style); if (action == null) throw new IllegalArgumentException(); this.action = action; this.display = Display.getCurrent(); if (this.display == null) throw new IllegalArgumentException( "ActionAdapter has to be created in display thread"); //$NON-NLS-1$ action.addPropertyChangeListener(this); loadImage(); } /** Set the actions icon as {@link ImageDescriptor} if possible */ private void loadImage() { Object icon = action.getValue(javax.swing.Action.SMALL_ICON); if (icon instanceof ImageIcon) { try { setImageDescriptor( ImageDescriptor.createFromImageData(SwingRcpUtilities.convertToSWT((ImageIcon) icon))); } catch (Exception e) { _log.warn("Error converting action icon", e); // $NON-NLS-1$ } } } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#getDescription() */ @Override public String getDescription() { return (String) action.getValue(javax.swing.Action.LONG_DESCRIPTION); } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#getText() */ @Override public String getText() { Object text = action.getValue(javax.swing.Action.NAME); if (text == null) return null; else return text.toString(); } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#getToolTipText() */ @Override public String getToolTipText() { return (String) action.getValue(javax.swing.Action.SHORT_DESCRIPTION); } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#isEnabled() */ @Override public boolean isEnabled() { return action.isEnabled(); } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#setEnabled(boolean) */ @Override public void setEnabled(final boolean enabled) { final boolean old = isEnabled(); action.setEnabled(enabled); display.asyncExec( new Runnable() { @Override public void run() { firePropertyChange("enabled", old, enabled); // $NON-NLS-1$ } }); } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#setText(java.lang.String) */ @Override public void setText(final String text) { if (action != null) { final String old = getText(); action.putValue(javax.swing.Action.NAME, text); display.asyncExec( new Runnable() { @Override public void run() { firePropertyChange("text", old, text); // $NON-NLS-1$ } }); } } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#setToolTipText(java.lang.String) */ @Override public void setToolTipText(final String toolTipText) { final String old = getToolTipText(); action.putValue(javax.swing.Action.SHORT_DESCRIPTION, toolTipText); display.asyncExec( new Runnable() { @Override public void run() { firePropertyChange(Action.TOOL_TIP_TEXT, old, toolTipText); } }); } /* * (non-Javadoc) * * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { // execute action SwingUtilities.invokeLater( new Runnable() { /* * (non-Javadoc) * * @see java.lang.Runnable#run() */ @Override public void run() { action.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null)); } }); } /* * (non-Javadoc) * * @see java.beans.PropertyChangeListener#propertyChange(java.beans. * PropertyChangeEvent) */ @Override public void propertyChange(final PropertyChangeEvent evt) { // propagate property change event // -> enabled if (evt.getPropertyName().equals("enabled")) // $NON-NLS-1$ display.asyncExec( new Runnable() { @Override public void run() { firePropertyChange(Action.ENABLED, evt.getOldValue(), evt.getNewValue()); } }); } }
/** * Group implementation based on {@link ODocument}s * * @author Simon Templer * @partner 01 / Fraunhofer Institute for Computer Graphics Research */ public class OGroup implements MutableGroup { private static final ALogger log = ALoggerFactory.getLogger(OGroup.class); /** The set of special field names, e.g. for the binary wrapper field */ private static final Set<String> SPECIAL_FIELDS = new HashSet<String>(); static { SPECIAL_FIELDS.add(OSerializationHelper.BINARY_WRAPPER_FIELD); SPECIAL_FIELDS.add(OSerializationHelper.FIELD_SERIALIZATION_TYPE); SPECIAL_FIELDS.add(OSerializationHelper.FIELD_CONVERT_ID); SPECIAL_FIELDS.add(OSerializationHelper.FIELD_CRS_ID); SPECIAL_FIELDS.add(OSerializationHelper.FIELD_STRING_VALUE); SPECIAL_FIELDS.add(OSerializationHelper.FIELD_COLLECTION_TYPE); SPECIAL_FIELDS.add(OSerializationHelper.FIELD_VALUES); } /** The document backing the group */ protected final ODocument document; /** The associated database record. */ protected ODatabaseRecord db; /** The definition group */ private final DefinitionGroup definition; /** * Creates an empty group with an associated definition group. * * @param definition the associated group */ public OGroup(DefinitionGroup definition) { document = new ODocument(); this.definition = definition; } /** * Configure the internal document with the given database and return it * * @param db the database * @return the internal document configured with the database */ public ODocument configureDocument(ODatabaseRecord db) { ODatabaseRecordThreadLocal.INSTANCE.set(db); configureDocument(document, db, definition); return document; } /** * Get the internal document. * * @return the internal document */ public ODocument getDocument() { return document; } private void configureDocument( ORecordAbstract<?> document, ODatabaseRecord db, DefinitionGroup definition) { // configure document // as of OrientDB 1.0rc8 the database may no longer be set on the // document // instead the current database can be set using // ODatabaseRecordThreadLocal.INSTANCE.set(db); // document.setDatabase(db); if (document instanceof ODocument) { // reset class name ODocument doc = (ODocument) document; /* * Attention: Two long class names cause problems as file names will * be based on them. */ String className = null; if (definition != null) { className = ONamespaceMap.encode(determineName(definition)); } else if (doc.containsField(OSerializationHelper.BINARY_WRAPPER_FIELD) || doc.containsField(OSerializationHelper.FIELD_SERIALIZATION_TYPE)) { className = OSerializationHelper.BINARY_WRAPPER_CLASSNAME; } if (className != null) { OSchema schema = db.getMetadata().getSchema(); if (!schema.existsClass(className)) { // if the class doesn't exist yet, create a physical cluster // manually for it int cluster = db.addCluster(className, CLUSTER_TYPE.PHYSICAL); schema.createClass(className, cluster); } doc.setClassName(className); } // configure children for (Entry<String, Object> field : doc) { List<ODocument> docs = new ArrayList<ODocument>(); List<ORecordAbstract<?>> recs = new ArrayList<ORecordAbstract<?>>(); if (field.getValue() instanceof Collection<?>) { for (Object value : (Collection<?>) field.getValue()) { if (value instanceof ODocument && !getSpecialFieldNames().contains(field.getKey())) { docs.add((ODocument) value); } else if (value instanceof ORecordAbstract<?>) { recs.add((ORecordAbstract<?>) value); } } } else if (field.getValue() instanceof ODocument && !getSpecialFieldNames().contains(field.getKey())) { docs.add((ODocument) field.getValue()); } else if (field.getValue() instanceof ORecordAbstract<?>) { recs.add((ORecordAbstract<?>) field.getValue()); } if (definition != null) { for (ODocument valueDoc : docs) { ChildDefinition<?> child = definition.getChild(decodeProperty(field.getKey())); DefinitionGroup childGroup; if (child.asProperty() != null) { childGroup = child.asProperty().getPropertyType(); } else if (child.asGroup() != null) { childGroup = child.asGroup(); } else { throw new IllegalStateException( "Document is associated neither with a property nor a property group."); } configureDocument(valueDoc, db, childGroup); } } for (ORecordAbstract<?> fieldRec : recs) { configureDocument(fieldRec, db, null); } } } } /** * Determine the name to use for a definition group as class name to encode. * * @param definition the definition group * @return the name to encode as class name */ private static QName determineName(DefinitionGroup definition) { if (definition instanceof Definition) { return ((Definition<?>) definition).getName(); } return new QName(definition.getIdentifier()); } /** * Creates a group based on the given document * * @param document the document * @param definition the definition of the associated group * @param db the database */ public OGroup(ODocument document, DefinitionGroup definition, ODatabaseRecord db) { this.document = document; this.definition = definition; this.db = db; } /** * Copy constructor. Creates a group based on the properties and values of the given group. * * @param org the instance to copy */ public OGroup(Group org) { this(org.getDefinition()); for (QName property : org.getPropertyNames()) { setProperty(property, org.getProperty(property).clone()); } } /** @see MutableGroup#addProperty(QName, Object) */ @Override public void addProperty(QName propertyName, Object value) { addProperty(propertyName, value, document); } /** * Adds a property value to a given {@link ODocument} * * @param propertyName the property name * @param value the property value * @param document the {link ODocument} where the value is to add */ @SuppressWarnings("unchecked") protected void addProperty(QName propertyName, Object value, ODocument document) { boolean isInstanceDocument = document == this.document; // convert instances to documents value = convertInstance(value); String pName = encodeProperty(propertyName); boolean collection = !isInstanceDocument || isCollectionProperty(propertyName); if (collection) { // combine value with previous ones Object oldValue = document.field(pName); if (oldValue == null) { // default: use list List<Object> valueList = new ArrayList<Object>(); valueList.add(value); document.field( pName, valueList, (isInstanceDocument) ? (getCollectionType(propertyName)) : (OType.EMBEDDEDLIST)); } else if (oldValue instanceof Collection<?>) { // add value to collection ((Collection<Object>) oldValue).add(value); } else if (oldValue.getClass().isArray()) { // create new array Object[] oldArray = (Object[]) oldValue; Object[] values = new Object[oldArray.length + 1]; System.arraycopy(oldArray, 0, values, 0, oldArray.length); values[oldArray.length] = value; document.field( pName, values, (isInstanceDocument) ? (getCollectionType(propertyName)) : (OType.EMBEDDEDLIST)); } } else { // just set the field document.field(pName, value); } } /** * Get the OrientDB collection type for the given property name * * @param propertyName the property name * @return the collection type, either {@link OType#EMBEDDEDLIST} or {@link OType#LINKLIST} */ private OType getCollectionType(QName propertyName) { ChildDefinition<?> child = definition.getChild(propertyName); if (child != null) { if (child.asProperty() != null) { TypeDefinition propType = child.asProperty().getPropertyType(); if (propType.getConstraint(HasValueFlag.class).isEnabled()) { return OType.EMBEDDEDLIST; } else { return OType.LINKLIST; } } else if (child.asGroup() != null) { // values must be OGroups return OType.LINKLIST; } } // default to embedded llist return OType.EMBEDDEDLIST; } /** * Converts {@link Group}s and {@link Instance}s to {@link ODocument} but leaves other objects * untouched. * * @param value the object to convert * @return the converted object */ protected Object convertInstance(Object value) { return OSerializationHelper.convertForDB(value); } /** * Determines if a property can have multiple values * * @param propertyName the property name * @return if the property can have multiple values */ private boolean isCollectionProperty(QName propertyName) { // ChildDefinition<?> child = definition.getChild(propertyName); // long max; // if (child instanceof PropertyDefinition) { // max = ((PropertyDefinition) child).getConstraint(Cardinality.class).getMaxOccurs(); // } // else if (child instanceof GroupPropertyDefinition) { // max = ((GroupPropertyDefinition) child).getConstraint(Cardinality.class).getMaxOccurs(); // } // else { // // default to true // return true; // } // // return max == Cardinality.UNBOUNDED || max > 1; // XXX treat everything as a collection property, as we may deal with // merged instances return true; } /** @see MutableGroup#setProperty(QName, Object[]) */ @Override public void setProperty(QName propertyName, Object... values) { setPropertyInternal(this.document, propertyName, values); } /** * Sets values for a property in a certain ODocument * * @param propertyName the property name * @param values the values for the property * @param document the document which should contain the data */ protected void setPropertyInternal(ODocument document, QName propertyName, Object... values) { String pName = encodeProperty(propertyName); if (values == null || values.length == 0) { document.removeField(pName); return; } boolean collection = isCollectionProperty(propertyName); if (!collection) { if (values.length > 1) { // TODO log type and property log.warn( "Attempt to set multiple values on a property that supports only one, using only the first value"); } document.field(pName, convertInstance(values[0])); } else { List<Object> valueList = new ArrayList<Object>(); for (Object value : values) { valueList.add(convertInstance(value)); } document.field(pName, valueList, getCollectionType(propertyName)); } } /** * Encode a qualified property name to a string * * @param propertyName the qualified property name * @return the name encoded as a single string */ protected String encodeProperty(QName propertyName) { // encode name & map namespace return ONamespaceMap.encode(propertyName); } /** * Decode an encoded property name to a qualified name * * @param encodedProperty the encoded property name * @return the qualified property name */ protected QName decodeProperty(String encodedProperty) { try { // decode name & unmap namespace return ONamespaceMap.decode(encodedProperty); } catch (Throwable e) { throw new RuntimeException("Could not encode property name", e); } } /** @see Instance#getProperty(QName) */ @Override public Object[] getProperty(QName propertyName) { return getProperty(propertyName, this.document); } /** * Gets a property value from a given {@link ODocument} * * @param propertyName the property name * @param document the {link ODocument} which contains the property * @return an Array of Objects containing the needed property */ protected Object[] getProperty(QName propertyName, ODocument document) { associatedDbWithThread(); String pName = encodeProperty(propertyName); Object value = document.field(pName); if (value == null) { return null; } // cannot check for Iterable as ODocument is also an Iterable else if (value instanceof Collection<?> || value.getClass().isArray()) { List<Object> valueList = new ArrayList<Object>(); for (Object val : (Iterable<?>) value) { valueList.add(convertDocument(val, propertyName)); } return valueList.toArray(); } else { return new Object[] {convertDocument(value, propertyName)}; } } /** Associate the database with the current thread (if set on the group) */ protected void associatedDbWithThread() { if (db != null) { ODatabaseRecordThreadLocal.INSTANCE.set(db); } } /** * Converts {@link ODocument}s to {@link Instance}s but leaves other objects untouched. * * @param value the object to convert * @param propertyName the name of the property the value is associated with * @return the converted object */ protected Object convertDocument(Object value, QName propertyName) { return OSerializationHelper.convertFromDB(value, this, propertyName); } /** @see Group#getPropertyNames() */ @Override public Iterable<QName> getPropertyNames() { return getPropertyNames(this.document); } /** * Returns the index keys of a certain ODocument * * @param document the keys are retrieved from * @return an Iterable with the keys as QNames */ protected Iterable<QName> getPropertyNames(ODocument document) { associatedDbWithThread(); Set<String> fields = new HashSet<String>(Arrays.asList(document.fieldNames())); // remove value field fields.removeAll(getSpecialFieldNames()); Set<QName> qFields = new HashSet<QName>(); for (String field : fields) { qFields.add(decodeProperty(field)); } return qFields; } /** * Get the special field names, e.g. for metadata. * * @return the collection of special field names. */ protected Collection<String> getSpecialFieldNames() { return SPECIAL_FIELDS; } /** @see Group#getDefinition() */ @Override public DefinitionGroup getDefinition() { return definition; } /** * Get the associated database. * * @return the associated database record */ public ODatabaseRecord getDb() { return db; } }
/** * Abstract I/O wizard based on {@link IOProvider} descriptors * * @param <P> the {@link IOProvider} type used in the wizard * @author Simon Templer * @partner 01 / Fraunhofer Institute for Computer Graphics Research */ public abstract class IOWizard<P extends IOProvider> extends Wizard implements IPageChangingListener { private static final ALogger log = ALoggerFactory.getLogger(IOWizard.class); private final Set<IOWizardListener<P, ? extends IOWizard<P>>> listeners = new HashSet<IOWizardListener<P, ? extends IOWizard<P>>>(); private final Class<P> providerType; private P provider; private IOProviderDescriptor descriptor; private IOAdvisor<P> advisor; private String actionId; private IContentType contentType; private Multimap<String, AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> configPages; private final List<IWizardPage> mainPages = new ArrayList<IWizardPage>(); /** * Create an I/O wizard * * @param providerType the I/O provider type */ public IOWizard(Class<P> providerType) { super(); this.providerType = providerType; // create possible configuration pages configPages = createConfigurationPages(getFactories()); setNeedsProgressMonitor(true); } /** * Get the I/O advisor * * @return the advisor */ protected IOAdvisor<P> getAdvisor() { return advisor; } /** * Get the action identifier * * @return the action ID */ public String getActionId() { return actionId; } /** * Set the I/O advisor * * @param advisor the advisor to set * @param actionId the action identifier, <code>null</code> if it has none */ public void setAdvisor(IOAdvisor<P> advisor, String actionId) { this.advisor = advisor; this.actionId = actionId; // recreate possible configuration pages now that advisor is set configPages = createConfigurationPages(getFactories()); } /** * Get the provider IDs mapped to configuration pages. * * @param factories the provider descriptors * @return provider IDs mapped to configuration pages */ protected ListMultimap<String, AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> createConfigurationPages(Collection<IOProviderDescriptor> factories) { return ConfigurationPageExtension.getInstance().getConfigurationPages(getFactories()); } /** @see Wizard#addPages() */ @Override public void addPages() { super.addPages(); // add configuration pages for (AbstractConfigurationPage<? extends P, ? extends IOWizard<P>> page : new HashSet<>(configPages.values())) { addPage(page); } if (getContainer() instanceof WizardDialog) { ((WizardDialog) getContainer()).addPageChangingListener(this); } else { throw new RuntimeException("Only WizardDialog as container supported"); } } /** @see IPageChangingListener#handlePageChanging(PageChangingEvent) */ @Override public void handlePageChanging(PageChangingEvent event) { if (getProvider() == null) { return; } if (event.getCurrentPage() instanceof IWizardPage && event.getTargetPage() == getNextPage((IWizardPage) event.getCurrentPage())) { // only do automatic configuration when proceeding to next page if (event.getCurrentPage() instanceof IOWizardPage<?, ?>) { @SuppressWarnings("unchecked") IOWizardPage<P, ?> page = (IOWizardPage<P, ?>) event.getCurrentPage(); event.doit = validatePage(page); // TODO error message?! } } } /** @see Wizard#dispose() */ @Override public void dispose() { if (getContainer() instanceof WizardDialog) { ((WizardDialog) getContainer()).removePageChangingListener(this); } super.dispose(); } /** @see Wizard#addPage(IWizardPage) */ @Override public void addPage(IWizardPage page) { // collect main pages if (!configPages.containsValue(page)) { mainPages.add(page); } super.addPage(page); } /** * Get the list of configuration pages for the currently selected provider factory <code>null * </code> if there are none. * * @return the configuration pages for the current provider */ protected List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> getConfigurationPages() { if (descriptor == null) { return null; } // get the provider id String id = descriptor.getIdentifier(); List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> result = new ArrayList<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>>( configPages.get(id)); return (result.size() > 0 ? result : null); } /** @see Wizard#canFinish() */ @Override public boolean canFinish() { // check if main pages are complete for (int i = 0; i < mainPages.size(); i++) { if (!(mainPages.get(i)).isPageComplete()) { return false; } } // check if configuration pages are complete List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> confPages = getConfigurationPages(); if (confPages != null) { for (int i = 0; i < confPages.size(); i++) { if (!(confPages.get(i)).isPageComplete()) { return false; } } } return true; } /** @see Wizard#getNextPage(IWizardPage) */ @Override public IWizardPage getNextPage(IWizardPage page) { // get main index int mainIndex = mainPages.indexOf(page); if (mainIndex >= 0) { // current page is one of the main pages if (mainIndex < mainPages.size() - 1) { // next main page return mainPages.get(mainIndex + 1); } else { // first configuration page List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> confPages = getConfigurationPages(); if (confPages != null && confPages.size() > 0) { return confPages.get(0); } } } else { // current page is a configuration page List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> confPages = getConfigurationPages(); // return the next configuration page if (confPages != null) { for (int i = 0; i < confPages.size() - 1; ++i) { if (confPages.get(i) == page) { return confPages.get(i + 1); } } } } return null; } /** @see Wizard#getPageCount() */ @Override public int getPageCount() { int count = mainPages.size(); List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> confPages = getConfigurationPages(); if (confPages != null) { count += confPages.size(); } return count; } /** @see Wizard#getPreviousPage(IWizardPage) */ @Override public IWizardPage getPreviousPage(IWizardPage page) { // get main index int mainIndex = mainPages.indexOf(page); if (mainIndex >= 0) { // current page is one of the main pages if (mainIndex > 0) { // previous main page return mainPages.get(mainIndex - 1); } } else { // current page is a configuration page List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> confPages = getConfigurationPages(); if (confPages != null) { if (confPages.size() > 0 && confPages.get(0) == page) { if (mainPages.isEmpty()) { return null; } else { // return last main page return mainPages.get(mainPages.size() - 1); } } // return the previous configuration page for (int i = 1; i < confPages.size(); ++i) { if (confPages.get(i) == page) { return confPages.get(i - 1); } } } } return null; } /** @see Wizard#getStartingPage() */ @Override public IWizardPage getStartingPage() { if (!mainPages.isEmpty()) { return mainPages.get(0); } else { List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> cps = getConfigurationPages(); if (cps != null && !cps.isEmpty()) { return cps.get(0); } else { // TODO provide an empty completed page instead? throw new IllegalStateException("No starting page to display for wizard"); } } } /** * Get the available provider descriptors. To filter or sort them you can override this method. * * @return the available provider descriptors */ public List<IOProviderDescriptor> getFactories() { // FIXME rename method return IOProviderExtension.getInstance() .getFactories( new FactoryFilter<IOProvider, IOProviderDescriptor>() { @Override public boolean acceptFactory(IOProviderDescriptor factory) { // accept all factories that provide a compatible I/O // provider return providerType.isAssignableFrom(factory.getProviderType()); } @Override public boolean acceptCollection( ExtensionObjectFactoryCollection<IOProvider, IOProviderDescriptor> collection) { return true; } }); } /** * Get the provider assigned to the wizard. It will be <code>null</code> if no page assigned a * provider factory to the wizard yet. * * @return the I/O provider */ @SuppressWarnings("unchecked") public P getProvider() { if (provider == null && descriptor != null) { try { provider = (P) descriptor.createExtensionObject(); } catch (Exception e) { throw new IllegalStateException("Could not instantiate I/O provider", e); } advisor.prepareProvider(provider); } return provider; } /** * Assign an I/O provider factory to the wizard * * @param descriptor the provider factory to set */ public void setProviderFactory(IOProviderDescriptor descriptor) { /* * The following must be done even if the descriptor seems the same, as * the descriptor might be a preset and thus influence the configuration * pages. */ // disable old configuration pages List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> pages = getConfigurationPages(); if (pages != null) { for (AbstractConfigurationPage<? extends P, ? extends IOWizard<P>> page : pages) { page.disable(); } } this.descriptor = descriptor; // reset provider provider = null; // enable new configuration pages pages = getConfigurationPages(); if (pages != null) { for (AbstractConfigurationPage<? extends P, ? extends IOWizard<P>> page : pages) { page.enable(); } } // force button update try { getContainer().updateButtons(); } catch (NullPointerException e) { // ignore - buttons may not have been initialized yet } fireProviderFactoryChanged(descriptor); } /** * Get the content type assigned to the wizard * * @return the content type, may be <code>null</code> */ public IContentType getContentType() { return contentType; } /** * Assign a content type to the wizard * * @param contentType the content type to set */ public void setContentType(IContentType contentType) { if (Objects.equal(contentType, this.contentType)) return; this.contentType = contentType; fireContentTypeChanged(contentType); } /** * Get the provider descriptor assigned to the wizard. It will be <code>null</code> if no page * assigned a provider factory to the wizard yet. * * @return the I/O provider factory */ public IOProviderDescriptor getProviderFactory() { return descriptor; } /** * @see Wizard#performFinish() * @return <code>true</code> if executing the I/O provider was successful */ @Override public boolean performFinish() { if (getProvider() == null) { return false; } if (!applyConfiguration()) { return false; } // create default report IOReporter defReport = provider.createReporter(); // validate and execute provider try { // validate configuration provider.validate(); ProjectService ps = PlatformUI.getWorkbench().getService(ProjectService.class); URI projectLoc = ps.getLoadLocation() == null ? null : ps.getLoadLocation(); boolean isProjectResource = false; if (actionId != null) { // XXX instead move project resource to action? ActionUI factory = ActionUIExtension.getInstance().findActionUI(actionId); isProjectResource = factory.isProjectResource(); } // prevent loading of duplicate resources if (isProjectResource && provider instanceof ImportProvider) { String currentResource = ((ImportProvider) provider).getSource().getLocation().toString(); URI currentAbsolute = URI.create(currentResource); if (projectLoc != null && !currentAbsolute.isAbsolute()) currentAbsolute = projectLoc.resolve(currentAbsolute); for (IOConfiguration conf : ((Project) ps.getProjectInfo()).getResources()) { Value otherResourceValue = conf.getProviderConfiguration().get(ImportProvider.PARAM_SOURCE); if (otherResourceValue == null) continue; String otherResource = otherResourceValue.as(String.class); URI otherAbsolute = URI.create(otherResource); if (projectLoc != null && !otherAbsolute.isAbsolute()) otherAbsolute = projectLoc.resolve(otherAbsolute); String action = conf.getActionId(); // resource is already loaded into the project if (currentAbsolute.equals(otherAbsolute) && Objects.equal(actionId, action)) { log.userError("Resource is already loaded. Loading duplicate resources is aborted!"); return false; } } } // enable provider internal caching if (isProjectResource && provider instanceof CachingImportProvider) { ((CachingImportProvider) provider).setProvideCache(); } IOReport report = execute(provider, defReport); if (report != null) { // add report to report server ReportService repService = PlatformUI.getWorkbench().getService(ReportService.class); repService.addReport(report); // show message to user if (report.isSuccess()) { // no message, we rely on the report being shown/processed // let advisor handle results try { getContainer() .run( true, false, new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { monitor.beginTask("Completing operation...", IProgressMonitor.UNKNOWN); try { advisor.handleResults(getProvider()); } finally { monitor.done(); } } }); } catch (InvocationTargetException e) { log.userError( "Error processing results:\n" + e.getCause().getLocalizedMessage(), e.getCause()); return false; } catch (Exception e) { log.userError("Error processing results:\n" + e.getLocalizedMessage(), e); return false; } // add to project service if necessary if (isProjectResource) ps.rememberIO(actionId, getProviderFactory().getIdentifier(), provider); return true; } else { // error message log.userError(report.getSummary() + "\nPlease see the report for details."); return false; } } else return true; } catch (IOProviderConfigurationException e) { // user feedback log.userError( "Validation of the provider configuration failed:\n" + e.getLocalizedMessage(), e); return false; } } /** * Apply configuration of main pages, configuration pages and the wizard. * * @return <code>true</code> if validation was successful, <code>false</code> otherwise */ protected boolean applyConfiguration() { // process main pages for (int i = 0; i < mainPages.size(); i++) { // validating is still necessary as it is not guaranteed to be up to // date by handlePageChanging boolean valid = validatePage(mainPages.get(i)); if (!valid) { // TODO error message?! return false; } } // check if configuration pages are complete List<AbstractConfigurationPage<? extends P, ? extends IOWizard<P>>> confPages = getConfigurationPages(); if (confPages != null) { for (int i = 0; i < confPages.size(); i++) { // validating is still necessary as it is not guaranteed to be // up to date by handlePageChanging boolean valid = validatePage(confPages.get(i)); if (!valid) { // TODO error message?! return false; } } } // process wizard updateConfiguration(provider); return true; } /** * Execute the given provider * * @param provider the I/O provider * @param defaultReporter the default reporter that is used if the provider doesn't supply a * report * @return the execution report, if null it will not give feedback to the user and the advisor's * handleResult method won't be called either */ protected IOReport execute(final IOProvider provider, final IOReporter defaultReporter) { // execute provider final AtomicReference<IOReport> report = new AtomicReference<IOReport>(defaultReporter); defaultReporter.setSuccess(false); try { getContainer() .run( true, provider.isCancelable(), new IRunnableWithProgress() { @Override public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { ATransaction trans = log.begin(defaultReporter.getTaskName()); try { IOReport result = provider.execute(new ProgressMonitorIndicator(monitor)); if (result != null) { report.set(result); } else { defaultReporter.setSuccess(true); } } catch (Throwable e) { defaultReporter.error(new IOMessageImpl(e.getLocalizedMessage(), e)); } finally { trans.end(); } } }); } catch (Throwable e) { defaultReporter.error(new IOMessageImpl(e.getLocalizedMessage(), e)); } return report.get(); } /** * Update the provider configuration. This will be called just before the I/O provider is * executed. * * @param provider the I/O provider */ protected void updateConfiguration(P provider) { // set the content type provider.setContentType(getContentType()); // let advisor update configuration advisor.updateConfiguration(provider); } /** * Validate the given page and update the I/O provider * * @param page the wizard page to validate * @return if the page is valid and updating the I/O provider was successful */ @SuppressWarnings("unchecked") protected boolean validatePage(IWizardPage page) { if (page instanceof IOWizardPage<?, ?>) { return ((IOWizardPage<P, ?>) page).updateConfiguration(getProvider()); } else { return true; } } /** * Get the supported I/O provider type, usually an interface. * * @return the supported I/O provider type */ public Class<P> getProviderType() { return providerType; } /** * Adds an {@link IOWizardListener} * * @param listener the listener to add */ public void addIOWizardListener(IOWizardListener<P, ? extends IOWizard<P>> listener) { synchronized (listeners) { listeners.add(listener); } } /** * Removes an {@link IOWizardListener} * * @param listener the listener to remove */ public void removeIOWizardListener(IOWizardListener<P, ? extends IOWizard<P>> listener) { synchronized (listeners) { listeners.remove(listener); } } private void fireProviderFactoryChanged(IOProviderDescriptor providerFactory) { synchronized (listeners) { for (IOWizardListener<P, ? extends IOWizard<P>> listener : listeners) { listener.providerDescriptorChanged(providerFactory); } } } private void fireContentTypeChanged(IContentType contentType) { synchronized (listeners) { for (IOWizardListener<P, ? extends IOWizard<P>> listener : listeners) { listener.contentTypeChanged(contentType); } } } }
/** * {@link IOAdvisor} extension * * @author Simon Templer */ public class ActionUIExtension extends AbstractExtension<IOWizard<?>, ActionUI> { private static final ALogger log = ALoggerFactory.getLogger(ActionUIExtension.class); /** Factory for {@link IOAdvisor}s based on a {@link IConfigurationElement} */ private static class ConfigurationFactory extends AbstractConfigurationFactory<IOWizard<?>> implements ActionUI { private boolean advisorInitialized = false; private ActionUIAdvisor<?> actionAdvisor; /** * Create a factory based on the given configuration element * * @param conf the configuration */ protected ConfigurationFactory(IConfigurationElement conf) { super(conf, "wizard"); } /** @see ActionUI#getEnabledWhen() */ @Override public Expression getEnabledWhen() { IConfigurationElement[] children = conf.getChildren("enabledWhen"); if (children != null && children.length > 0) { // get child of enabled when children = children[0].getChildren(); if (children != null && children.length > 0) { try { return ElementHandler.getDefault() .create(ExpressionConverter.getDefault(), children[0]); } catch (CoreException e) { log.error("Could not evaluate expression for action enablement.", e); } } } return null; } @Override public ActionUIAdvisor<?> getUIAdvisor() { if (!advisorInitialized) { try { if (conf.getAttribute("ui-advisor") != null) { Class<?> advisorClass = ExtensionUtil.loadClass(conf, "ui-advisor"); actionAdvisor = (ActionUIAdvisor<?>) advisorClass.newInstance(); } } catch (Exception e) { log.error("Failed to created action UI advisor instance", e); } advisorInitialized = true; } return actionAdvisor; } /** @see ExtensionObjectFactory#dispose(Object) */ @Override public void dispose(IOWizard<?> wizard) { // do nothing } /** @see ExtensionObjectDefinition#getDisplayName() */ @Override public String getDisplayName() { return conf.getAttribute("label"); } /** @see ExtensionObjectDefinition#getIdentifier() */ @Override public String getIdentifier() { return conf.getAttribute("id"); } /** @see ActionUI#getDisabledReason() */ @Override public String getDisabledReason() { IConfigurationElement[] children = conf.getChildren("enabledWhen"); if (children != null && children.length > 0) { // get child of enabled when return children[0].getAttribute("disabledReason"); } return null; } /** @see AbstractConfigurationFactory#createExtensionObject() */ @Override public IOWizard<?> createExtensionObject() throws Exception { IOWizard<?> wizard = super.createExtensionObject(); String customTitle = getCustomTitle(); if (customTitle != null && !customTitle.isEmpty()) { wizard.setWindowTitle(customTitle); } return wizard; } /** @see AbstractObjectDefinition#getPriority() */ @Override public int getPriority() { try { return Integer.parseInt(conf.getAttribute("priority")); } catch (NumberFormatException e) { return 0; } } /** @see AbstractObjectFactory#getIconURL() */ @Override public URL getIconURL() { return getIconURL("icon"); } /** @see ActionUI#isProjectResource() */ @Override public boolean isProjectResource() { return Boolean.parseBoolean(conf.getAttribute("projectResource")); } /** @see ActionUI#getActionID() */ @Override public String getActionID() { return conf.getAttribute("action"); } /** @see ActionUI#getCustomTitle() */ @Override public String getCustomTitle() { return conf.getAttribute("customTitle"); } } /** The extension point ID */ public static final String ID = "eu.esdihumboldt.hale.ui.io.action"; private static ActionUIExtension instance; /** * Get the extension instance * * @return the instance */ public static ActionUIExtension getInstance() { if (instance == null) { instance = new ActionUIExtension(); } return instance; } /** Default constructor */ private ActionUIExtension() { super(ID); } /** @see AbstractExtension#createFactory(IConfigurationElement) */ @Override protected ActionUI createFactory(IConfigurationElement conf) throws Exception { if (conf.getName().equals("action-ui")) { return new ConfigurationFactory(conf); } return null; } /** * Find the {@link ActionUI} associated with a certain action * * @param actionId the action identifier * @return the action UI or <code>null</code> */ public ActionUI findActionUI(final String actionId) { List<ActionUI> factories = getFactories( new FactoryFilter<IOWizard<?>, ActionUI>() { @Override public boolean acceptFactory(ActionUI factory) { return factory.getActionID().equals(actionId); } @Override public boolean acceptCollection( ExtensionObjectFactoryCollection<IOWizard<?>, ActionUI> collection) { return true; } }); if (factories == null || factories.isEmpty()) { return null; } // XXX what if there are multiple ActionUIs for an action? return factories.get(0); } }