/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.ui.service.align.internal;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import eu.esdihumboldt.hale.common.align.extension.function.custom.CustomPropertyFunction;
import eu.esdihumboldt.hale.common.align.model.Alignment;
import eu.esdihumboldt.hale.common.align.model.AlignmentUtil;
import eu.esdihumboldt.hale.common.align.model.BaseAlignmentCell;
import eu.esdihumboldt.hale.common.align.model.Cell;
import eu.esdihumboldt.hale.common.align.model.ModifiableCell;
import eu.esdihumboldt.hale.common.align.model.MutableAlignment;
import eu.esdihumboldt.hale.common.align.model.MutableCell;
import eu.esdihumboldt.hale.common.align.model.Priority;
import eu.esdihumboldt.hale.common.align.model.TransformationMode;
import eu.esdihumboldt.hale.common.align.model.impl.DefaultAlignment;
import eu.esdihumboldt.hale.ui.service.align.AlignmentService;
import eu.esdihumboldt.hale.ui.service.align.AlignmentServiceAdapter;
import eu.esdihumboldt.hale.ui.service.align.BaseAlignmentLoader;
import eu.esdihumboldt.hale.ui.service.project.ProjectService;
import eu.esdihumboldt.hale.ui.service.project.ProjectServiceAdapter;
/**
* Default {@link AlignmentService} implementation
*
* @author Thorsten Reitz
* @author Simon Templer
* @partner 01 / Fraunhofer Institute for Computer Graphics Research
*/
public class AlignmentServiceImpl extends AbstractAlignmentService {
private MutableAlignment alignment;
/**
* Default constructor
*
* @param projectService the project service
*/
public AlignmentServiceImpl(final ProjectService projectService) {
super();
alignment = new DefaultAlignment();
projectService.addListener(new ProjectServiceAdapter() {
@Override
public void onClean() {
clean();
}
});
// inform project service on alignment changes
addListener(new AlignmentServiceAdapter() {
@Override
public void alignmentCleared() {
projectService.setChanged();
}
@Override
public void cellsRemoved(Iterable<Cell> cell) {
projectService.setChanged();
}
@Override
public void cellsReplaced(Map<? extends Cell, ? extends Cell> cells) {
projectService.setChanged();
}
@Override
public void cellsAdded(Iterable<Cell> cells) {
projectService.setChanged();
}
@Override
public void alignmentChanged() {
projectService.setChanged();
}
@Override
public void cellsPropertyChanged(Iterable<Cell> cells, String propertyName) {
projectService.setChanged();
}
@Override
public void customFunctionsChanged() {
projectService.setChanged();
}
});
}
/**
* @see AlignmentService#addCell(MutableCell)
*/
@Override
public void addCell(MutableCell cell) {
synchronized (this) {
alignment.addCell(cell);
}
notifyCellsAdded(Collections.singletonList((Cell) cell));
}
@Override
public void addCustomPropertyFunction(CustomPropertyFunction function) {
alignment.addCustomPropertyFunction(function);
notifyCustomFunctionsChanged();
}
@Override
public void removeCustomPropertyFunction(String id) {
alignment.removeCustomPropertyFunction(id);
notifyCustomFunctionsChanged();
}
/**
* @see AlignmentService#replaceCell(Cell, MutableCell)
*/
@Override
public void replaceCell(Cell oldCell, MutableCell newCell) {
synchronized (this) {
alignment.removeCell(oldCell);
alignment.addCell(newCell);
}
notifyCellReplaced(oldCell, newCell);
}
/**
* @see eu.esdihumboldt.hale.ui.service.align.AlignmentService#replaceCells(java.util.Map)
*/
@Override
public void replaceCells(Map<? extends Cell, MutableCell> cells) {
synchronized (this) {
for (Entry<? extends Cell, MutableCell> e : cells.entrySet()) {
alignment.removeCell(e.getKey());
alignment.addCell(e.getValue());
}
}
notifyCellsReplaced(cells);
}
/**
* @see AlignmentService#clean()
*/
@Override
public void clean() {
synchronized (this) {
alignment = new DefaultAlignment();
}
notifyAlignmentCleared();
}
/**
* @see AlignmentService#addOrUpdateAlignment(MutableAlignment)
*/
@Override
public void addOrUpdateAlignment(MutableAlignment alignment) {
Collection<Cell> added = new ArrayList<Cell>();
/*
* XXX Updates for disables not supported
*
* One problem is, that base alignments shouldn't be added twice, so if
* the given alignment contains a base alignment that is also contained
* in the current alignment it won't be added.
*
* Now what about cells in the given alignment, that reference those
* base alignments (-> disabled for)? They need to be changed to the
* base alignment cells of the current alignment.
*
* Those cells can be obtained by replacing the prefix part of the cell
* and using getCell(String).
*
* XXX Addition of base alignments not supported
*
* Here the problem is, that it is not enough to gather the base
* alignment cells and change their prefix to a new one, since the base
* cell could be another base alignment cell, which would need to
* change, too. They would have to be added in order. An option would be
* to load them with the base alignment loading mechanism, which would
* result in a loss of additional disables.
*
* XXX Changes of conflicting base alignment cells not handled
*/
// if (!alignment.getBaseAlignments().isEmpty()) {
// _log.warn("Adding alignments currently does not support merging of base alignments. Import base alignments independently.");
// }
boolean addedBaseCells = false;
// add cells
synchronized (this) {
for (Cell cell : alignment.getCells()) {
if (cell instanceof MutableCell) {
this.alignment.addCell((MutableCell) cell);
added.add(cell);
}
else if (!(cell instanceof BaseAlignmentCell)) {
throw new IllegalStateException(
"The given alignment contained a cell which is neither mutable nor from a base alignment.");
}
else {
addedBaseCells = true;
}
}
}
synchronized (this) {
// XXX this needs merging, see above
boolean first = true;
for (Entry<String, URI> baseAlignment : alignment.getBaseAlignments().entrySet()) {
Collection<CustomPropertyFunction> baseFunctions;
if (first) {
// XXX hack - insert all base functions with the first base
// alignment
baseFunctions = alignment.getBasePropertyFunctions().values();
}
else {
// base functions should only be added once
baseFunctions = Collections.<CustomPropertyFunction> emptyList();
}
this.alignment.addBaseAlignment(baseAlignment.getKey(), baseAlignment.getValue(),
alignment.getBaseAlignmentCells(baseAlignment.getValue()), //
baseFunctions);
}
}
// add custom functions
boolean addedFunctions = false;
synchronized (this) {
for (CustomPropertyFunction cf : alignment.getCustomPropertyFunctions().values()) {
this.alignment.addCustomPropertyFunction(cf);
addedFunctions = true;
}
}
if (addedBaseCells || (!added.isEmpty() && addedFunctions)) {
// only emit one combined event (not to trigger multiple
// transformations)
notifyAlignmentChanged();
}
else {
// emit individual events
if (!added.isEmpty()) {
notifyCellsAdded(added);
}
if (addedFunctions) {
notifyCustomFunctionsChanged();
}
}
}
/**
* @see AlignmentService#getAlignment()
*/
@Override
public Alignment getAlignment() {
return alignment;
}
/**
* @see AlignmentService#removeCells(Cell[])
*/
@Override
public void removeCells(Cell... cells) {
if (cells == null || cells.length == 0) {
return;
}
List<Cell> removed = new ArrayList<Cell>();
synchronized (this) {
for (Cell cell : cells) {
if (alignment.removeCell(cell)) {
removed.add(cell);
}
}
}
notifyCellsRemoved(removed);
}
/**
* @see eu.esdihumboldt.hale.ui.service.align.AlignmentService#setCellProperty(java.lang.String,
* java.lang.String, java.lang.Object)
*/
@Override
public void setCellProperty(String cellId, String propertyName, Object property) {
if (propertyName == null || property == null) {
throw new IllegalArgumentException("Mandatory parameter is null");
}
Cell cell = getAlignment().getCell(cellId);
if (cell instanceof ModifiableCell && (Cell.PROPERTY_DISABLE_FOR.equals(propertyName)
|| Cell.PROPERTY_ENABLE_FOR.equals(propertyName))) {
boolean disable = Cell.PROPERTY_DISABLE_FOR.equals(propertyName);
if (property instanceof Cell) {
Cell other = (Cell) property;
if (!AlignmentUtil.isTypeCell(other))
throw new IllegalArgumentException();
// This call may fail, if the cell was disabled in a base
// alignment and someone tries to enable it again.
((ModifiableCell) cell).setDisabledFor(other, disable);
}
else
throw new IllegalArgumentException();
notifyCellsPropertyChanged(Arrays.asList(cell), propertyName);
}
else if (Cell.PROPERTY_TRANSFORMATION_MODE.equals(propertyName)) {
// set the transformation mode
if (cell instanceof ModifiableCell && property instanceof TransformationMode) {
ModifiableCell modCell = (ModifiableCell) cell;
modCell.setTransformationMode((TransformationMode) property);
notifyCellsPropertyChanged(Arrays.asList(cell), propertyName);
}
}
else if (cell instanceof MutableCell) {
MutableCell mutableCell = (MutableCell) cell;
if (Cell.PROPERTY_PRIORITY.equals(propertyName)) {
if (property instanceof Priority) {
Priority priority = (Priority) property;
mutableCell.setPriority(priority);
}
if (property instanceof String) {
String priorityStr = (String) property;
Priority priority = Priority.valueOf(priorityStr);
if (priority != null) {
mutableCell.setPriority(priority);
}
else {
throw new IllegalArgumentException();
}
}
notifyCellsPropertyChanged(Arrays.asList(cell), propertyName);
}
}
else {
throw new IllegalArgumentException("No mutable cell by the given id found: " + cellId);
}
}
/**
* @see eu.esdihumboldt.hale.ui.service.align.AlignmentService#addBaseAlignment(eu.esdihumboldt.hale.ui.service.align.BaseAlignmentLoader)
*/
@Override
public boolean addBaseAlignment(BaseAlignmentLoader loader) {
boolean success;
synchronized (this) {
success = loader.load(alignment);
}
if (success)
notifyAlignmentChanged();
return success;
}
}