Example #1
0
/**
 * 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.");
  }
}
Example #2
0
/**
 * 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";
  }
}
Example #3
0
/**
 * 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));
  }
}
Example #4
0
/**
 * 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());
            }
          });
  }
}
Example #5
0
/**
 * 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;
  }
}
Example #6
0
/**
 * 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);
      }
    }
  }
}
Example #7
0
/**
 * {@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);
  }
}