From d583547f3046d1e0d9a1dc689abe71dcd1decfb7 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Tue, 2 Feb 2021 12:30:23 +0100 Subject: [PATCH 1/1] Improve terms framework. --- .../src/org/argeo/entity/Term.java | 22 +++++++++ .../src/org/argeo/entity/TermsManager.java | 7 ++- .../src/org/argeo/entity/Typology.java | 14 ++++++ .../entity/ui/forms/AbstractTermsPart.java | 24 +++++---- .../argeo/entity/ui/forms/MultiTermsPart.java | 49 +++++++++++++------ .../argeo/entity/ui/forms/SingleTermPart.java | 26 +++++++--- .../src/org/argeo/suite/core/SuiteTerm.java | 25 ++++++---- .../argeo/suite/core/SuiteTermsManager.java | 15 ++++-- .../org/argeo/suite/core/SuiteTypology.java | 11 ++++- 9 files changed, 148 insertions(+), 45 deletions(-) create mode 100644 core/org.argeo.entity.api/src/org/argeo/entity/Term.java create mode 100644 core/org.argeo.entity.api/src/org/argeo/entity/Typology.java diff --git a/core/org.argeo.entity.api/src/org/argeo/entity/Term.java b/core/org.argeo.entity.api/src/org/argeo/entity/Term.java new file mode 100644 index 0000000..3bf075a --- /dev/null +++ b/core/org.argeo.entity.api/src/org/argeo/entity/Term.java @@ -0,0 +1,22 @@ +package org.argeo.entity; + +import java.util.List; + +/** + * A name within a {@link Typology}, used to qualify an entity (categories, + * keywords, etc.). + */ +public interface Term { + String getId(); + + String getName(); + +// String getRelativePath(); + + Typology getTypology(); + + List getSubTerms(); + + Term getParentTerm(); + +} diff --git a/core/org.argeo.entity.api/src/org/argeo/entity/TermsManager.java b/core/org.argeo.entity.api/src/org/argeo/entity/TermsManager.java index a2b5951..7564ff9 100644 --- a/core/org.argeo.entity.api/src/org/argeo/entity/TermsManager.java +++ b/core/org.argeo.entity.api/src/org/argeo/entity/TermsManager.java @@ -4,5 +4,10 @@ import java.util.List; /** Provides optimised access and utilities around terms typologies. */ public interface TermsManager { - List listAllTerms(String typology); + Typology getTypology(String typology); + + Term getTerm(String id); + + List listAllTerms(String typology); + } diff --git a/core/org.argeo.entity.api/src/org/argeo/entity/Typology.java b/core/org.argeo.entity.api/src/org/argeo/entity/Typology.java new file mode 100644 index 0000000..43431a0 --- /dev/null +++ b/core/org.argeo.entity.api/src/org/argeo/entity/Typology.java @@ -0,0 +1,14 @@ +package org.argeo.entity; + +import java.util.List; + +/** A structured and exhaustive set of {@link Term}s. */ +public interface Typology { + + String getId(); + + boolean isFlat(); + + List getSubTerms(); + +} diff --git a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java index f2ea756..408a0bf 100644 --- a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java +++ b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/AbstractTermsPart.java @@ -2,12 +2,15 @@ package org.argeo.entity.ui.forms; import javax.jcr.Item; +import org.argeo.cms.Localized; import org.argeo.cms.ui.CmsTheme; import org.argeo.cms.ui.util.CmsIcon; import org.argeo.cms.ui.viewers.EditablePart; import org.argeo.cms.ui.widgets.ContextOverlay; import org.argeo.cms.ui.widgets.StyledControl; +import org.argeo.entity.Term; import org.argeo.entity.TermsManager; +import org.argeo.entity.Typology; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.layout.GridData; @@ -20,7 +23,7 @@ import org.eclipse.swt.widgets.ToolItem; public abstract class AbstractTermsPart extends StyledControl implements EditablePart { private static final long serialVersionUID = -5497097995341927710L; protected final TermsManager termsManager; - protected final String typology; + protected final Typology typology; protected final boolean editable; @@ -33,13 +36,12 @@ public abstract class AbstractTermsPart extends StyledControl implements Editabl protected final CmsTheme theme; - public AbstractTermsPart(Composite parent, int style, Item item, TermsManager termsManager, - String typology) { + public AbstractTermsPart(Composite parent, int style, Item item, TermsManager termsManager, String typology) { super(parent, style, item); - if(item==null) + if (item == null) throw new IllegalArgumentException("Item cannot be null"); this.termsManager = termsManager; - this.typology = typology; + this.typology = termsManager.getTypology(typology); this.theme = CmsTheme.getCmsTheme(parent); editable = !(SWT.READ_ONLY == (style & SWT.READ_ONLY)); highlightColor = parent.getDisplay().getSystemColor(SWT.COLOR_GRAY); @@ -55,17 +57,21 @@ public abstract class AbstractTermsPart extends StyledControl implements Editabl } - protected String getTermLabel(String name) { - return name; + protected String getTermLabel(Term term) { + if (term instanceof Localized) + return ((Localized) term).lead(); + else + return term.getName(); + } protected abstract void refresh(ContextOverlay contextArea, String filter, Text txt); - protected boolean isTermSelectable(String term) { + protected boolean isTermSelectable(Term term) { return true; } - protected void processTermListLabel(String term, Label label) { + protected void processTermListLabel(Term term, Label label) { } diff --git a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java index 1a12e90..457da70 100644 --- a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java +++ b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/MultiTermsPart.java @@ -12,6 +12,7 @@ import org.argeo.cms.ui.widgets.ContextOverlay; import org.argeo.eclipse.ui.MouseDoubleClick; import org.argeo.eclipse.ui.MouseDown; import org.argeo.eclipse.ui.Selected; +import org.argeo.entity.Term; import org.argeo.entity.TermsManager; import org.argeo.jcr.Jcr; import org.eclipse.swt.SWT; @@ -39,9 +40,9 @@ public class MultiTermsPart extends AbstractTermsPart { Composite placeholder = new Composite(box, SWT.NONE); RowLayout rl = new RowLayout(SWT.HORIZONTAL | SWT.WRAP); placeholder.setLayout(rl); - List currentValue = Jcr.getMultiple(getNode(), typology); + List currentValue = getValue(); if (currentValue != null && !currentValue.isEmpty()) - for (String value : currentValue) { + for (Term value : currentValue) { Composite block = new Composite(placeholder, SWT.NONE); block.setLayout(CmsUiUtils.noSpaceGridLayout(3)); Label lbl = new Label(block, SWT.SINGLE); @@ -58,14 +59,13 @@ public class MultiTermsPart extends AbstractTermsPart { styleDelete(deleteItem); deleteItem.addSelectionListener((Selected) (e) -> { // we retrieve them again here because they may have changed - List curr = Jcr.getMultiple(getNode(), typology); - List newValue = new ArrayList<>(); - for (String v : curr) { + List curr = getValue(); + List newValue = new ArrayList<>(); + for (Term v : curr) { if (!v.equals(value)) newValue.add(v); } - Jcr.set(getNode(), typology, newValue); - Jcr.save(getNode()); + setValue(newValue); block.dispose(); layout(true, true); }); @@ -143,9 +143,9 @@ public class MultiTermsPart extends AbstractTermsPart { @Override protected void refresh(ContextOverlay contextArea, String filter, Text txt) { CmsUiUtils.clear(contextArea); - List terms = termsManager.listAllTerms(typology); - List currentValue = Jcr.getMultiple(getNode(), typology); - terms: for (String term : terms) { + List terms = termsManager.listAllTerms(typology.getId()); + List currentValue = getValue(); + terms: for (Term term : terms) { if (currentValue != null && currentValue.contains(term)) continue terms; String display = getTermLabel(term); @@ -156,13 +156,12 @@ public class MultiTermsPart extends AbstractTermsPart { processTermListLabel(term, termL); if (isTermSelectable(term)) termL.addMouseListener((MouseDown) (e) -> { - List newValue = new ArrayList<>(); - List curr = Jcr.getMultiple(getNode(), typology); + List newValue = new ArrayList<>(); + List curr = getValue(); if (currentValue != null) newValue.addAll(curr); newValue.add(term); - Jcr.set(getNode(), typology, newValue); - Jcr.save(getNode()); + setValue(newValue); contextArea.hide(); stopEditing(); }); @@ -170,4 +169,26 @@ public class MultiTermsPart extends AbstractTermsPart { contextArea.show(); } + protected List getValue() { + String property = typology.getId(); + List curr = Jcr.getMultiple(getNode(), property); + List res = new ArrayList<>(); + if (curr != null) + for (String str : curr) { + Term term = termsManager.getTerm(str); + res.add(term); + } + return res; + } + + protected void setValue(List value) { + String property = typology.getId(); + List ids = new ArrayList<>(); + for (Term term : value) { + ids.add(term.getId()); + } + Jcr.set(getNode(), property, ids); + Jcr.save(getNode()); + } + } diff --git a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java index e9fad04..0dcf13d 100644 --- a/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java +++ b/core/org.argeo.entity.ui/src/org/argeo/entity/ui/forms/SingleTermPart.java @@ -11,6 +11,7 @@ import org.argeo.cms.ui.widgets.ContextOverlay; import org.argeo.eclipse.ui.MouseDoubleClick; import org.argeo.eclipse.ui.MouseDown; import org.argeo.eclipse.ui.Selected; +import org.argeo.entity.Term; import org.argeo.entity.TermsManager; import org.argeo.jcr.Jcr; import org.eclipse.swt.SWT; @@ -47,8 +48,7 @@ public class SingleTermPart extends AbstractTermsPart { ToolItem deleteItem = new ToolItem(toolBar, SWT.PUSH); styleDelete(deleteItem); deleteItem.addSelectionListener((Selected) (e) -> { - Jcr.set(getNode(), typology, null); - Jcr.save(getNode()); + setValue(null); stopEditing(); }); ToolItem cancelItem = new ToolItem(toolBar, SWT.PUSH); @@ -95,7 +95,7 @@ public class SingleTermPart extends AbstractTermsPart { } else { Composite block = new Composite(box, SWT.NONE); block.setLayout(CmsUiUtils.noSpaceGridLayout(2)); - String currentValue = Jcr.get(getNode(), typology); + Term currentValue = getValue(); if (currentValue != null) { Label lbl = new Label(block, SWT.SINGLE); String display = getTermLabel(currentValue); @@ -120,8 +120,8 @@ public class SingleTermPart extends AbstractTermsPart { @Override protected void refresh(ContextOverlay contextArea, String filter, Text txt) { CmsUiUtils.clear(contextArea); - List terms = termsManager.listAllTerms(typology); - terms: for (String term : terms) { + List terms = termsManager.listAllTerms(typology.getId()); + terms: for (Term term : terms) { String display = getTermLabel(term); if (filter != null && !display.toLowerCase().contains(filter)) continue terms; @@ -130,8 +130,7 @@ public class SingleTermPart extends AbstractTermsPart { processTermListLabel(term, termL); if (isTermSelectable(term)) termL.addMouseListener((MouseDown) (e) -> { - Jcr.set(getNode(), typology, term); - Jcr.save(getNode()); + setValue(term); contextArea.hide(); stopEditing(); }); @@ -140,4 +139,17 @@ public class SingleTermPart extends AbstractTermsPart { // txt.setFocus(); } + protected Term getValue() { + String property = typology.getId(); + String id = Jcr.get(getNode(), property); + Term term = termsManager.getTerm(id); + + return term; + } + + protected void setValue(Term value) { + String property = typology.getId(); + Jcr.set(getNode(), property, value != null ? value.getId() : null); + Jcr.save(getNode()); + } } diff --git a/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTerm.java b/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTerm.java index 0c03dc5..227b567 100644 --- a/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTerm.java +++ b/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTerm.java @@ -3,10 +3,12 @@ package org.argeo.suite.core; import java.util.ArrayList; import java.util.List; +import org.argeo.entity.Term; + /** * A single term. Helper to optimise {@link SuiteTermsManager} implementation. */ -class SuiteTerm { +class SuiteTerm implements Term { private final String name; private final String relativePath; private final SuiteTypology typology; @@ -21,13 +23,19 @@ class SuiteTerm { this.relativePath = relativePath; int index = relativePath.lastIndexOf('/'); if (index > 0) { - this.name = relativePath.substring(index); + this.name = relativePath.substring(index + 1); } else { this.name = relativePath; } id = typology.getName() + '/' + relativePath; } + @Override + public String getId() { + return id; + } + + @Override public String getName() { return name; } @@ -36,19 +44,18 @@ class SuiteTerm { return relativePath; } - SuiteTypology getTypology() { + @Override + public SuiteTypology getTypology() { return typology; } - public String getId() { - return id; - } - - List getSubTerms() { + @Override + public List getSubTerms() { return subTerms; } - SuiteTerm getParentTerm() { + @Override + public SuiteTerm getParentTerm() { return parentTerm; } diff --git a/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTermsManager.java b/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTermsManager.java index 1362f94..f8822f7 100644 --- a/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTermsManager.java +++ b/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTermsManager.java @@ -14,6 +14,7 @@ import org.argeo.api.NodeConstants; import org.argeo.api.NodeUtils; import org.argeo.entity.EntityNames; import org.argeo.entity.EntityType; +import org.argeo.entity.Term; import org.argeo.entity.TermsManager; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; @@ -32,16 +33,22 @@ public class SuiteTermsManager implements TermsManager { } @Override - public List listAllTerms(String typology) { - List res = new ArrayList<>(); + public List listAllTerms(String typology) { + List res = new ArrayList<>(); SuiteTypology t = getTypology(typology); for (SuiteTerm term : t.getAllTerms()) { - res.add(term.getId()); + res.add(term); } return res; } - SuiteTypology getTypology(String typology) { + @Override + public SuiteTerm getTerm(String termId) { + return terms.get(termId); + } + + @Override + public SuiteTypology getTypology(String typology) { SuiteTypology t = typologies.get(typology); if (t == null) { Node termsNode = Jcr.getNode(adminSession, "SELECT * FROM [{0}] WHERE NAME()=\"{1}\"", diff --git a/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTypology.java b/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTypology.java index e84066c..d192ed7 100644 --- a/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTypology.java +++ b/core/org.argeo.suite.core/src/org/argeo/suite/core/SuiteTypology.java @@ -5,10 +5,12 @@ import java.util.List; import javax.jcr.Node; +import org.argeo.entity.Term; +import org.argeo.entity.Typology; import org.argeo.jcr.Jcr; /** A typology. Helper to optimise {@link SuiteTermsManager} implementation. */ -class SuiteTypology { +class SuiteTypology implements Typology { private final String name; private final Node node; private boolean isFlat = true; @@ -20,6 +22,11 @@ class SuiteTypology { this.name = Jcr.getName(this.node); } + @Override + public String getId() { + return name; + } + public String getName() { return name; } @@ -33,10 +40,12 @@ class SuiteTypology { isFlat = false; } + @Override public boolean isFlat() { return isFlat; } + @Override public List getSubTerms() { return subTerms; } -- 2.30.2