blob: 4d978ffb2218f1eec58413df67b986249f874628 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2013 Formal Mind GmbH and University of Dusseldorf.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michael Jastram - initial API and implementation
******************************************************************************/
package org.eclipse.rmf.reqif10.pror.editor.agilegrid;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.agilemore.agilegrid.AbstractContentProvider;
import org.agilemore.agilegrid.AgileGrid;
import org.agilemore.agilegrid.IContentProvider;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.rmf.reqif10.AttributeValue;
import org.eclipse.rmf.reqif10.Identifiable;
import org.eclipse.rmf.reqif10.ReqIF;
import org.eclipse.rmf.reqif10.SpecElementWithAttributes;
import org.eclipse.rmf.reqif10.SpecHierarchy;
import org.eclipse.rmf.reqif10.SpecObject;
import org.eclipse.rmf.reqif10.SpecRelation;
import org.eclipse.rmf.reqif10.Specification;
import org.eclipse.rmf.reqif10.XhtmlContent;
import org.eclipse.rmf.reqif10.common.util.ProrXhtmlSimplifiedHelper;
import org.eclipse.rmf.reqif10.common.util.ReqIF10Util;
import org.eclipse.rmf.reqif10.pror.configuration.Column;
import org.eclipse.rmf.reqif10.pror.configuration.ProrSpecViewConfiguration;
import org.eclipse.rmf.reqif10.pror.configuration.UnifiedColumn;
import org.eclipse.rmf.reqif10.pror.editor.agilegrid.ProrRow.ProrRowSpecHierarchy;
import org.eclipse.rmf.reqif10.pror.editor.agilegrid.ProrRow.ProrRowSpecRelation;
import org.eclipse.rmf.reqif10.pror.filter.ReqifFilter;
/**
* This ContentProvider manages a {@link Specification}, to be displayed in an
* {@link AgileGrid}.
*/
public class ProrAgileGridContentProvider extends AbstractContentProvider {
private final Specification root;
final ProrSpecViewConfiguration specViewConfig;
private ArrayList<ProrRow> cache = null;
private Map<Identifiable, ProrRow> rowMap = new HashMap<Identifiable, ProrRow>();
private boolean showSpecRelations;
private ReqifFilter filter;
public ProrAgileGridContentProvider(Specification specification,
ProrSpecViewConfiguration specViewConfig) {
this.root = specification;
this.specViewConfig = specViewConfig;
// TODO We want to be more nuanced.
specification.eAdapters().add(new EContentAdapter() {
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);
if (notification.getEventType() == Notification.ADD
|| notification.getEventType() == Notification.ADD_MANY
|| notification.getEventType() == Notification.MOVE
|| notification.getEventType() == Notification.REMOVE
|| notification.getEventType() == Notification.REMOVE_MANY
|| notification.getEventType() == Notification.SET
|| notification.getEventType() == Notification.UNSET) {
flushCache();
}
}
});
}
/**
* Sets a filter. The null argument resets filtering.
*/
public void setFilter(ReqifFilter filter) {
if (filter != this.filter) {
this.filter = filter;
flushCache();
}
}
/**
* Returns the {@link AttributeValue} for the given column for the element
* associated with the row. May return null.
*/
@Override
public Object doGetContentAt(int row, int col)
throws IndexOutOfBoundsException {
if (row >= getCache().size()) {
throw new IndexOutOfBoundsException("Row does not exist: " + row);
}
ProrRow prorRow = getCache().get(row);
if (!prorRow.isVisible()) {
return null;
}
SpecElementWithAttributes element = prorRow.getSpecElement();
if (col == specViewConfig.getColumns().size()) {
// For the Link column, we return the linked element.
return element instanceof SpecElementWithAttributes ? element
: null;
} else if (col <= specViewConfig.getColumns().size()) {
// we return the AttributeValue.
return getValueForColumn(element, row, col);
} else {
throw new IndexOutOfBoundsException("Column does not exist: " + col);
}
}
/**
* Changes the Value through the editing domain if it has changed.
* <p>
*
* We don't need to change anything here, as changing the
* {@link AttributeValue} automagically updates the model.
*/
@Override
public void doSetContentAt(int row, int col, Object newValue) {
}
/**
* Whether to show {@link SpecRelation}s as part of the Content.
*
* @param status
*/
public void setShowSpecRelations(boolean status) {
this.showSpecRelations = status;
for (ProrRow row : getCache()) {
if (row instanceof ProrRowSpecHierarchy)
((ProrRowSpecHierarchy) row).setShowSpecRelation(status);
}
flushCache();
}
/**
* Whether to show {@link SpecRelation}s as part of the Content.
*/
public boolean getShowSpecRelations() {
return this.showSpecRelations;
}
/**
* Finds the Object for the given row, which may be a SpecHierarchy or
* SpecRelation.
*/
ProrRow getProrRow(int row) {
if (row >= 0) {
return getCache().get(row);
}
return null;
}
public void flushCache() {
cache = null;
}
/**
* Uses a Job to provider feedback to the user.
*
* @return
*/
private ArrayList<ProrRow> getCache() {
if (cache == null) {
ArrayList<ProrRow> tmpCache = new ArrayList<ProrRow>();
recurseSpecHierarchyForRow(0, 0, root.getChildren(), tmpCache);
cache = tmpCache;
}
return cache;
}
private ProrRow getProrRowForSpecElement(Identifiable e, int row, int level) {
ProrRow prorRow = rowMap.get(e);
if (prorRow == null) {
prorRow = ProrRow.createProrRow(e, row, level);
rowMap.put(e, prorRow);
} else {
prorRow.setLevel(row);
prorRow.setLevel(level);
}
return prorRow;
}
/**
*
* @param current
* The current counter
* @param elements
* The {@link SpecHierarchy}s to traverse, Can be SpecHierarchies
* or SpecRelations
* @param tmpCache
* @return either the {@link ProrRow} with the given row, or the new current
* row
*/
private int recurseSpecHierarchyForRow(int current, int depth,
List<SpecHierarchy> elements, ArrayList<ProrRow> tmpCache) {
for (SpecHierarchy element : elements) {
ProrRowSpecHierarchy prorRowSH = (ProrRowSpecHierarchy) getProrRowForSpecElement(
element, current, depth);
if (filter != null && !filter.match(element.getObject())) {
prorRowSH.setVisible(false);
} else {
prorRowSH.setVisible(true);
}
tmpCache.add(current, prorRowSH);
if (prorRowSH.isShowSpecRelation()) {
for (SpecRelation specRelation : getSpecRelationsFor(element)) {
++current;
ProrRowSpecRelation prorRowSR = (ProrRowSpecRelation) getProrRowForSpecElement(
specRelation, current, depth + 1);
tmpCache.add(current, prorRowSR);
}
}
int result = recurseSpecHierarchyForRow(++current, depth + 1,
element.getChildren(), tmpCache);
current = result;
}
return current;
}
/**
* Returns the actual {@link AttributeValue} for the given Column and the
* given {@link SpecElementWithUserDefinedAttributes}
*/
AttributeValue getValueForColumn(SpecElementWithAttributes element,
int row, int col) {
// Knock-out criteria
if (element == null)
return null;
if (col >= specViewConfig.getColumns().size())
return null;
// Handle the Unified Column
Column column = specViewConfig.getColumns().get(col);
if (column instanceof UnifiedColumn) {
AttributeValue av = ReqIF10Util.getAttributeValueForLabel(element,
"ReqIF.ChapterName");
if (av != null && ReqIF10Util.getTheValue(av) != null) {
Object value = ReqIF10Util.getTheValue(av);
if (value instanceof XhtmlContent) {
XhtmlContent xhtmlContent = (XhtmlContent) value;
String s = ProrXhtmlSimplifiedHelper
.xhtmlToSimplifiedString(xhtmlContent);
if (s != null && s.trim().length() > 0) {
return av;
}
} else {
if (value.toString().trim().length() > 0) {
return av;
}
}
}
return ReqIF10Util.getAttributeValueForLabel(element, "ReqIF.Text");
}
String label = column.getLabel();
return ReqIF10Util.getAttributeValueForLabel(element, label);
}
/**
* Returns the SpecRelations that use the given SpecObject (via the given
* SpecHierarchy) as a source. This method checks {@link #showSpecRelations}
* and returns immediately if it is false.
*/
private List<SpecRelation> getSpecRelationsFor(SpecHierarchy specHierarchy) {
if (specHierarchy.getObject() == null)
return Collections.emptyList();
SpecObject source = specHierarchy.getObject();
ReqIF reqif = ReqIF10Util.getReqIF(source);
// Can happen if source is detached from the reqif model (e.g. just
// being deleted)
if (reqif == null)
return Collections.emptyList();
List<SpecRelation> list = new ArrayList<SpecRelation>();
for (SpecRelation relation : reqif.getCoreContent().getSpecRelations()) {
if (source.equals(relation.getSource())) {
list.add(relation);
}
}
return list;
}
void updateElement(SpecElementWithAttributes element) {
recurseUpdateElement(0, element, root.getChildren());
flushCache();
}
/**
* Recurses over all SpecHierarchies and updates wherever it finds the given
* specObject. As a specObject can appear multiple time, we have to cover
* the whole tree.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private int recurseUpdateElement(int row,
SpecElementWithAttributes element, List list) {
for (Object entry : list) {
List children = new ArrayList();
boolean refresh = false;
if (element instanceof SpecRelation && element.equals(entry)) {
refresh = true;
} else if (entry instanceof SpecHierarchy) {
SpecHierarchy specHierarchy = (SpecHierarchy) entry;
children.addAll(specHierarchy.getChildren());
ProrRow prorRow = rowMap.get(specHierarchy);
if (prorRow != null && prorRow instanceof ProrRowSpecHierarchy) {
if (((ProrRowSpecHierarchy) prorRow).isShowSpecRelation())
children.addAll(getSpecRelationsFor(specHierarchy));
}
if (element.equals(specHierarchy.getObject())) {
refresh = true;
}
}
// Workaround: provide null for "old value" to force a recognition
// of
// the change.
if (refresh) {
firePropertyChange(IContentProvider.Content, row, 0, null,
entry);
}
row++;
row = recurseUpdateElement(row, element, children);
}
return row;
}
public int getRowCount() {
return getCache().size();
}
}