Skip to content

Commit

Permalink
Correct count of label occurrences
Browse files Browse the repository at this point in the history
  • Loading branch information
rensink committed Aug 4, 2024
1 parent dd78cc1 commit 04cec85
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 31 deletions.
8 changes: 0 additions & 8 deletions src/main/java/nl/utwente/groove/gui/jgraph/AspectJEdge.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;

import org.eclipse.jdt.annotation.NonNull;
Expand Down Expand Up @@ -196,13 +195,6 @@ public Collection<? extends Label> getKeys() {
if (this.aspects.containsKey(Category.NESTING)) {
return Collections.emptySet();
} else {
Set<Label> result = new HashSet<>();
var s = getSourceVertex();
assert s != null;
result.addAll(s.getNodeKeys());
var t = getTargetVertex();
assert t != null;
result.addAll(t.getNodeKeys());
return super.getKeys();
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/nl/utwente/groove/gui/jgraph/AspectJVertex.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,29 @@ StringBuilder getNodeDescription() {
return result;
}

@Override
public Collection<? extends Label> getLabels() {
getNode().testFixed(true);
Collection<TypeElement> result = new ArrayList<>();
if (!getAspects().containsKey(Category.NESTING)) {
for (Edge edge : getEdges()) {
TypeEdge key = getKey(edge);
if (key != null) {
result.add(key);
}
}
// add additional self-edges
for (var edge : getExtraSelfEdges()) {
TypeEdge key = getKey(edge);
if (key != null) {
result.add(key);
}
}
result.addAll(getNodeKeys());
}
return result;
}

@Override
public Collection<? extends Label> getKeys() {
getNode().testFixed(true);
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/nl/utwente/groove/gui/jgraph/JCell.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ public interface JCell<G extends @NonNull Graph> extends GraphCell, Serializable
*/
public abstract Collection<? extends Label> getKeys();

/**
* Returns the set of labels that actually occur in this cell.
*/
default Collection<? extends Label> getLabels() {
return getKeys();
}

/**
* Returns the label tree key for a given graph edge wrapped by this JCell.
* @return the key for {@code edge}; if {@code null}, the edge
Expand Down
100 changes: 82 additions & 18 deletions src/main/java/nl/utwente/groove/gui/tree/LabelFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import nl.utwente.groove.gui.jgraph.JCell;
import nl.utwente.groove.gui.jgraph.JVertex;
import nl.utwente.groove.gui.look.VisualKey;
import nl.utwente.groove.util.Counter;
import nl.utwente.groove.util.Observable;
import nl.utwente.groove.util.line.Line;

Expand Down Expand Up @@ -78,7 +79,7 @@ public boolean addJCell(JCell<G> jCell) {
this.jCellEntryMap.put(jCell, entries);
// also modify the inverse map
for (Entry entry : entries) {
result |= this.entryJCellMap.get(entry).add(jCell);
result |= this.entryDataMap.get(entry).add(jCell);
}
}
return result;
Expand All @@ -93,7 +94,7 @@ public boolean removeJCell(JCell<G> jCell) {
Set<Entry> jCellEntries = this.jCellEntryMap.remove(jCell);
if (jCellEntries != null) {
for (Entry jCellEntry : jCellEntries) {
result |= this.entryJCellMap.get(jCellEntry).remove(jCell);
result |= this.entryDataMap.get(jCellEntry).remove(jCell);
}
}
return result;
Expand All @@ -114,13 +115,13 @@ public boolean modifyJCell(JCell<G> jCell) {
// remove the obsolete entries
for (Entry oldEntry : oldEntrySet) {
if (!newEntrySet.contains(oldEntry)) {
result |= this.entryJCellMap.get(oldEntry).remove(jCell);
result |= this.entryDataMap.get(oldEntry).remove(jCell);
}
}
// add the new entries
for (Entry newEntry : newEntrySet) {
if (!oldEntrySet.contains(newEntry)) {
result |= this.entryJCellMap.get(newEntry).add(jCell);
result |= this.entryDataMap.get(newEntry).add(jCell);
}
}
}
Expand All @@ -129,28 +130,32 @@ public boolean modifyJCell(JCell<G> jCell) {

/** Returns the set of {@link JCell}s for a given entry. */
public Set<JCell<G>> getJCells(Entry entry) {
return this.entryJCellMap.get(entry);
return this.entryDataMap.get(entry).jCells();
}

/** Returns the number of instances for a given entry. */
public int getCount(Entry entry) {
return this.entryDataMap.get(entry).count().value();
}

/** Indicates if there is at least one {@link JCell} with a given entry. */
public boolean hasJCells(Entry entry) {
Set<JCell<G>> jCells = getJCells(entry);
return jCells != null && !jCells.isEmpty();
return !getJCells(entry).isEmpty();
}

/**
* Clears the entire filter.
*/
public void clear() {
this.jCellEntryMap.clear();
this.entryJCellMap.clear();
this.entryDataMap.clear();
this.labelEntryMap.clear();
this.normalMap.clear();
}

/** Returns the set of all entries known to this filter. */
public Set<Entry> getEntries() {
return this.entryJCellMap.keySet();
return this.entryDataMap.keySet();
}

/** Lazily creates and returns a filter entry based on a given label key. */
Expand All @@ -163,7 +168,7 @@ public Entry getEntry(Label key) {
result = this.normalMap.get(result);
} else {
this.normalMap.put(result, result);
addEntry(result);
registerEntry(result);
}
this.labelEntryMap.put(key, result);
}
Expand All @@ -173,15 +178,15 @@ public Entry getEntry(Label key) {
/** Adds a newly created entry to the data structures of this filter.
* Should be called directly after creation of the entry.
*/
void addEntry(Entry entry) {
var old = this.entryJCellMap.put(entry, new HashSet<>());
void registerEntry(Entry entry) {
var old = this.entryDataMap.put(entry, new EntryData(entry));
assert old == null : "Duplicate label entry for %s (existing entry contained %s)"
.formatted(entry, old);
}

/** Mapping from entries to {@link JCell}s with that entry. */
private final Map<Entry,Set<JCell<G>>> entryJCellMap = new HashMap<>();
/** Inverse mapping of {@link #entryJCellMap}. */
private final Map<Entry,EntryData> entryDataMap = new HashMap<>();
/** Inverse mapping of {@link #entryDataMap}. */
private final Map<JCell<G>,Set<Entry>> jCellEntryMap = new HashMap<>();
/** Mapping from known labels to corresponding label entries. */
private final Map<Label,LabelEntry> labelEntryMap = new HashMap<>();
Expand Down Expand Up @@ -252,11 +257,10 @@ public void changeSelected(Collection<Entry> entries) {
*/
protected Set<JCell<G>> setSelection(Entry entry, boolean selected) {
Set<JCell<G>> result = Collections.<JCell<G>>emptySet();
var jCellsForEntry = this.entryJCellMap.get(entry);
assert jCellsForEntry != null : String
.format("Label %s unknown in map %s", entry, this.entryJCellMap);
var data = this.entryDataMap.get(entry);
assert data != null : String.format("Label %s unknown in map %s", entry, this.entryDataMap);
if (entry.setSelected(selected)) {
result = jCellsForEntry;
result = data.jCells();
}
return result;
}
Expand Down Expand Up @@ -317,6 +321,58 @@ public boolean isIncluded(JCell<G> jCell) {
private static VisualKey[] AFFECTED_KEYS
= {VisualKey.VISIBLE, VisualKey.LABEL, VisualKey.NODE_SIZE, VisualKey.TEXT_SIZE};

/** Record for the data stored for a given entry.
* We can't use Java records because they are static and therefore don't know the type parameter G
*/
class EntryData {
/**
* Creates a record for a given entry.
*/
public EntryData(Entry entry) {
this.entry = entry;
this.jCells = new HashSet<>();
this.count = new Counter();
}

private final Entry entry;

/** Returns the set of cells in this data object. */
Set<JCell<G>> jCells() {
return this.jCells;
}

private final Set<JCell<G>> jCells;

/** Returns the number of cells with this entry's label as primary key. */
Counter count() {
return this.count;
}

private final Counter count;

/** Updates the record by adding a given cell.
* @return {@code true} if the data was change by the operation
*/
boolean add(JCell<G> jCell) {
boolean result = this.jCells.add(jCell);
if (result && jCell.getLabels().stream().anyMatch(this.entry::matches)) {
this.count.increase();
}
return result;
}

/** Updates the record by removing a given cell.
* @return {@code true} if the data was change by the operation
*/
boolean remove(JCell<G> jCell) {
boolean result = this.jCells.remove(jCell);
if (result && jCell.getLabels().stream().anyMatch(this.entry::matches)) {
this.count.decrease();
}
return result;
}
}

/** Type of the keys in a label filter. */
public static interface Entry extends Comparable<Entry> {
/** Retrieves the line of the entry. */
Expand All @@ -339,6 +395,9 @@ public default boolean isPassive() {

/** Signals that this is a filter entry for nodes. */
public boolean isForNode();

/** Indicates if a given label corresponds to the one wrapped in this {@link Entry}. */
public boolean matches(Label label);
}

/** Filter entry wrapping a label. */
Expand Down Expand Up @@ -380,6 +439,11 @@ public boolean setSelected(boolean selected) {
/** Flag indicating if this entry is currently selected. */
private boolean selected;

@Override
public boolean matches(Label label) {
return label.getRole() == this.role && label.text().equals(toString());
}

@Override
public int compareTo(Entry o) {
var other = (LabelEntry) o;
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/nl/utwente/groove/gui/tree/LabelTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -645,10 +645,7 @@ public JComponent getTreeCellRendererComponent(@Nullable JTree tree, @Nullable O
labelIcon = entryNode.getIcon();
// set tool tip text
StringBuilder toolTipText = new StringBuilder();
Set<JCell<G>> occurrences = getFilter().getJCells(entry);
int count = occurrences == null
? 0
: occurrences.size();
int count = getFilter().getCount(entry);
toolTipText.append(count);
toolTipText.append(" occurrence");
if (count != 1) {
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/nl/utwente/groove/gui/tree/TypeFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ void update(TypeGraph typeGraph) {
if (this.entryMaps == null || typeGraph != this.entryMaps.typeGraph()) {
this.entryMaps = new EntryMap(typeGraph);
}
this.entryMaps.entryStream().forEach(this::addEntry);
this.entryMaps.entryStream().forEach(this::registerEntry);
this.stale = false;
}
assert typeGraph == this.entryMaps
Expand Down Expand Up @@ -257,6 +257,11 @@ public boolean isForNode() {
return getType() instanceof TypeNode;
}

@Override
public boolean matches(Label label) {
return getType().label().equals(label);
}

/** Returns the (subtype-closed) set of source and target (node) type entry. */
Set<TypeEntry> getNodes() {
return this.nodes;
Expand Down

0 comments on commit 04cec85

Please sign in to comment.