package de.ovgu.cide.af; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.core.runtime.CoreException; import cide.gast.ASTVisitor; import cide.gast.IASTNode; import cide.gparser.ParseException; import de.ovgu.cide.ASTColorChangedEvent; import de.ovgu.cide.CIDECorePlugin; import de.ovgu.cide.features.IFeatureModelWithID; import de.ovgu.cide.features.source.ColoredSourceFile; /** * Manager, der alternative Features eines ColoredSourceFile's verwaltet. Kann aus einem * ColoredSourceFile per getAltFeatureManager() geholt werden. * * @author Malte Rosenthal */ public class AlternativeFeatureManager { private ColoredSourceFile coloredSourceFile; private Map<String, List<Alternative>> id2alternatives; // Mapped eine ASTNode-ID auf eine Liste verf�gbarer Alternativen private final Map<String, IASTNode> id2node; // Mapped eine ASTNode-ID auf den AST-Knoten mit dieser ID public AlternativeFeatureManager(ColoredSourceFile coloredSourceFile) { this.coloredSourceFile = coloredSourceFile; id2node = new HashMap<String, IASTNode>(); } private void init() throws CoreException, ParseException { if (coloredSourceFile.isColored()) id2alternatives = coloredSourceFile.getColorManager().getAllAlternatives((IFeatureModelWithID) coloredSourceFile.getFeatureModel()); id2node.clear(); coloredSourceFile.getAST().accept(new ASTVisitor() { @Override public boolean visit(IASTNode node) { id2node.put(node.getId(), node); return true; } }); } /** * Gibt alle Alternativen aller AST-Knoten zur�ck. */ public Map<String, List<Alternative>> getAllAlternatives() { return coloredSourceFile.getColorManager().getAllAlternatives((IFeatureModelWithID) coloredSourceFile.getFeatureModel()); } /** * Gibt eine Liste von Alternativen zum AST-Knoten mit gegebener ID zur�ck, dessen Vater-Alternative aktiv ist. * * Dabei werden �nderungen an der aktiven Alternative, die noch nicht in die XML-Datei zur�ckgespeichert wurden, nicht ber�cksichtigt. * Steht die aktive Alternative also noch gar nicht in der XML-Datei, so wird sie hier auch nicht zur�ckgegeben. * @param id * @return * @throws CoreException * @throws ParseException */ public List<Alternative> getAlternativesWithActiveParent(String id) throws CoreException, ParseException { init(); if (id2alternatives == null) return null; List<Alternative> result = new LinkedList<Alternative>(); List<Alternative> allAlternatives = id2alternatives.get(id); if (allAlternatives != null) { for (Alternative alternative : allAlternatives) { if (alternative.parentIsActive()) result.add(alternative); } } return result; } /** * Gibt alle AST-Knoten zur�ck, zu denen es mindestens zwei Alternativen gibt, dessen Vater-Alternative aktiv ist. * @return * @throws CoreException * @throws ParseException */ public List<IASTNode> getAlternativeNodesWithActiveParent() throws CoreException, ParseException { init(); if (id2alternatives == null) return null; List<IASTNode> result = new LinkedList<IASTNode>(); for (Entry<String, List<Alternative>> entry : id2alternatives.entrySet()) { Map<Alternative, List<Alternative>> alternativesGroupedByParent = groupByParent(entry.getValue()); if (alternativesGroupedByParent != null) { for (Entry<Alternative, List<Alternative>> group : alternativesGroupedByParent.entrySet()) { if (group.getKey().isActive && (group.getValue() != null) && (group.getValue().size() > 1)) { result.add(id2node.get(entry.getKey())); break; } } } } return result; } public Map<Alternative, List<Alternative>> groupByParent(List<Alternative> alternatives) { if (alternatives == null) return null; Map<Alternative, List<Alternative>> result = new HashMap<Alternative, List<Alternative>>(); for (Alternative alternative : alternatives) { if (result.containsKey(alternative.getParent())) { result.get(alternative.getParent()).add(alternative); } else { List<Alternative> list = new LinkedList<Alternative>(); list.add(alternative); result.put(alternative.getParent(), list); } } return result; } /** * Gibt eine Liste von Alternativen zum gegebenen AST-Knoten innerhalb der gegebenen Eltern-Alternative zur�ck. * Dabei werden �nderungen an der aktiven Alternative, die noch nicht in die XML-Datei zur�ckgespeichert wurden, mitber�cksichtigt. * * @param node * @param parent * @return * @throws CoreException * @throws ParseException */ public List<Alternative> getAlternatives(IASTNode node, Alternative parent) throws CoreException, ParseException { if (node == null) return null; init(); List<Alternative> result = new LinkedList<Alternative>(); if (id2alternatives == null) { result.add(new Alternative(node, parent.getFeatures())); return result; } List<Alternative> alternatives = id2alternatives.get(node.getId()); Alternative activeAlternative = null; if (alternatives != null) { for (Alternative alternative : alternatives) { if (alternative.hasAncestor(parent)) { if (!alternative.isActive) { result.add(alternative); } else activeAlternative = alternative; } } } if (activeAlternative != null) { activeAlternative.update(coloredSourceFile, node); result.add(activeAlternative); } else { result.add(new Alternative(node, parent.getFeatures())); } return result; } /** * Wei� man, dass ein AST-Knoten in nur einer Alternative vorliegen kann (z.B. weil man keine Alternativen zu dem Knoten * angeben kann), so kann diese Alternative mit dieser Methode abgerufen werden. * * @param node * @param parent * @return * @throws CoreException * @throws ParseException */ public Alternative getTheAlternative(IASTNode node, Alternative parent) throws CoreException, ParseException { List<Alternative> alternatives = getAlternatives(node, parent); if ((alternatives != null) && (alternatives.size() == 1)) return alternatives.get(0); return null; } public void createAlternative(List<IASTNode> nodes, String altID) { if ((nodes == null) || nodes.isEmpty()) return; for (IASTNode node : nodes) { if (node != null) coloredSourceFile.getColorManager().createAlternative(node, altID); } CIDECorePlugin.getDefault().notifyListeners(new ASTColorChangedEvent(this, nodes, coloredSourceFile)); } /** * Diese Methode wird nach dem Wechseln zu einer anderen Alternative eines AST-Knotens aufgerufen. Die neue Alternative * wurde bereits ins Document geschrieben und gespeichert. Der hier �bergebene AST-Knoten ist aber der alte AST-Knoten, * also nicht mehr aktuell, weil beim Speichern ein neuer Parse-Lauf gestartet wird. * * @param alternative * @param node Alter AST-Knoten * @throws CoreException * @throws ParseException */ public void activateAlternative(Alternative alternative, final IASTNode node) throws CoreException, ParseException { if (node == null) return; coloredSourceFile.getColorManager().activateAlternative(alternative, node); CIDECorePlugin.getDefault().notifyListeners(new ASTColorChangedEvent(this, node, coloredSourceFile)); } }