diff --git a/CHANGELOG.md b/CHANGELOG.md index 14f0e536348..f26558eea07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,11 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - We renamed "database" to "library" to have a real distinction to SQL and NoSQL databases. [#2095](https://github.com/JabRef/jabref/issues/2095) - We added MathSciNet as a ID-based fetcher in the `BibTeX -> New entry` dialog (implements a [feature request in the forum](http://discourse.jabref.org/t/allow-to-search-by-mr-number-mathscinet)) - Removed the apache.commons.collections library +- We added a few properties to a group: + - Icon (with customizable color) that is shown in the groups panel (implements a [feature request in the forum](http://discourse.jabref.org/t/assign-colors-to-groups/321)). + - Description text that is shown on mouse hover (implements old feature requests [489](https://sourceforge.net/p/jabref/feature-requests/489/) and [818](https://sourceforge.net/p/jabref/feature-requests/818/) +- We introduced "automatic groups" that automatically create subgroups based on a certain criteria (e.g. a subgroup for every author or keyword). Implements [91](https://sourceforge.net/p/jabref/feature-requests/91/), [398](https://sourceforge.net/p/jabref/feature-requests/398/) and [#1173](https://github.com/JabRef/jabref/issues/1173). +- Expansion status of groups are saved across sessions. [#1428](https://github.com/JabRef/jabref/issues/1428) - We removed the ordinals-to-superscript formatter from the recommendations for biblatex save actions [#2596](https://github.com/JabRef/jabref/issues/2596) - The `Move linked files to default file directory`-Cleanup operation respects the `File directory pattern` setting - We separated the `Move file` and `Rename Pdfs` logic and context menu entries in the `General`-Tab for the Field `file` to improve the semantics diff --git a/src/main/java/org/jabref/logic/exporter/GroupSerializer.java b/src/main/java/org/jabref/logic/exporter/GroupSerializer.java index dc03fa0afe4..b174cfc31e0 100644 --- a/src/main/java/org/jabref/logic/exporter/GroupSerializer.java +++ b/src/main/java/org/jabref/logic/exporter/GroupSerializer.java @@ -2,12 +2,15 @@ import java.util.ArrayList; import java.util.List; -import java.util.Set; -import java.util.TreeSet; + +import javafx.scene.paint.Color; import org.jabref.logic.util.MetadataSerializationConfiguration; import org.jabref.model.groups.AbstractGroup; import org.jabref.model.groups.AllEntriesGroup; +import org.jabref.model.groups.AutomaticGroup; +import org.jabref.model.groups.AutomaticKeywordGroup; +import org.jabref.model.groups.AutomaticPersonsGroup; import org.jabref.model.groups.ExplicitGroup; import org.jabref.model.groups.GroupTreeNode; import org.jabref.model.groups.KeywordGroup; @@ -28,14 +31,8 @@ private String serializeExplicitGroup(ExplicitGroup group) { sb.append(group.getHierarchicalContext().ordinal()); sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); - // write legacy entry keys in well-defined order for CVS compatibility - Set sortedKeys = new TreeSet<>(); - sortedKeys.addAll(group.getLegacyEntryKeys()); + appendGroupDetails(sb, group); - for (String sortedKey : sortedKeys) { - sb.append(StringUtil.quote(sortedKey, MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR)); - sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); - } return sb.toString(); } @@ -55,6 +52,9 @@ private String serializeKeywordGroup(KeywordGroup group) { sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); sb.append(StringUtil.booleanToBinaryString(isRegex)); sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + + appendGroupDetails(sb, group); + return sb.toString(); } @@ -71,9 +71,22 @@ private String serializeSearchGroup(SearchGroup group) { sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); sb.append(StringUtil.booleanToBinaryString(group.isRegularExpression())); sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + + appendGroupDetails(sb, group); + return sb.toString(); } + private void appendGroupDetails(StringBuilder builder, AbstractGroup group) { + builder.append(StringUtil.booleanToBinaryString(group.isExpanded())); + builder.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + builder.append(group.getColor().map(Color::toString).orElse("")); + builder.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + builder.append(group.getIconCode().orElse("")); + builder.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + builder.append(group.getDescription().orElse("")); + builder.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + } /** * Returns a textual representation of this node and its children. This @@ -107,8 +120,41 @@ private String serializeGroup(AbstractGroup group) { return serializeKeywordGroup((KeywordGroup)group); } else if (group instanceof SearchGroup) { return serializeSearchGroup((SearchGroup)group); + } else if (group instanceof AutomaticKeywordGroup) { + return serializeAutomaticKeywordGroup((AutomaticKeywordGroup)group); + } else if (group instanceof AutomaticPersonsGroup) { + return serializeAutomaticPersonsGroup((AutomaticPersonsGroup)group); } else { throw new UnsupportedOperationException("Don't know how to serialize group" + group.getClass().getName()); } } + + private String serializeAutomaticPersonsGroup(AutomaticPersonsGroup group) { + StringBuilder sb = new StringBuilder(); + sb.append(MetadataSerializationConfiguration.AUTOMATIC_PERSONS_GROUP_ID); + appendAutomaticGroupDetails(sb, group); + sb.append(StringUtil.quote(group.getField(), MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR)); + sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + appendGroupDetails(sb, group); + return sb.toString(); + } + + private void appendAutomaticGroupDetails(StringBuilder builder, AutomaticGroup group) { + builder.append(StringUtil.quote(group.getName(), MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR)); + builder.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + builder.append(group.getHierarchicalContext().ordinal()); + builder.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + } + + private String serializeAutomaticKeywordGroup(AutomaticKeywordGroup group) { + StringBuilder sb = new StringBuilder(); + sb.append(MetadataSerializationConfiguration.AUTOMATIC_KEYWORD_GROUP_ID); + appendAutomaticGroupDetails(sb, group); + sb.append(StringUtil.quote(group.getField(), MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR)); + sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + sb.append(group.getKeywordSeperator()); + sb.append(MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR); + appendGroupDetails(sb, group); + return sb.toString(); + } } diff --git a/src/main/java/org/jabref/logic/importer/util/GroupsParser.java b/src/main/java/org/jabref/logic/importer/util/GroupsParser.java index c010d57e080..a769f6e7297 100644 --- a/src/main/java/org/jabref/logic/importer/util/GroupsParser.java +++ b/src/main/java/org/jabref/logic/importer/util/GroupsParser.java @@ -8,6 +8,8 @@ import org.jabref.logic.util.MetadataSerializationConfiguration; import org.jabref.logic.util.strings.QuotedStringTokenizer; import org.jabref.model.groups.AbstractGroup; +import org.jabref.model.groups.AutomaticKeywordGroup; +import org.jabref.model.groups.AutomaticPersonsGroup; import org.jabref.model.groups.ExplicitGroup; import org.jabref.model.groups.GroupHierarchyType; import org.jabref.model.groups.GroupTreeNode; @@ -84,7 +86,48 @@ public static AbstractGroup fromString(String s, Character keywordSeparator) if (s.startsWith(MetadataSerializationConfiguration.EXPLICIT_GROUP_ID)) { return GroupsParser.explicitGroupFromString(s, keywordSeparator); } - return null; // unknown group + if (s.startsWith(MetadataSerializationConfiguration.LEGACY_EXPLICIT_GROUP_ID)) { + return GroupsParser.legacyExplicitGroupFromString(s, keywordSeparator); + } + if (s.startsWith(MetadataSerializationConfiguration.AUTOMATIC_PERSONS_GROUP_ID)) { + return GroupsParser.automaticPersonsGroupFromString(s); + } + if (s.startsWith(MetadataSerializationConfiguration.AUTOMATIC_KEYWORD_GROUP_ID)) { + return GroupsParser.automaticKeywordGroupFromString(s); + } + + throw new ParseException("Unknown group: " + s); + } + + private static AbstractGroup automaticPersonsGroupFromString(String string) { + if (!string.startsWith(MetadataSerializationConfiguration.AUTOMATIC_PERSONS_GROUP_ID)) { + throw new IllegalArgumentException("KeywordGroup cannot be created from \"" + string + "\"."); + } + QuotedStringTokenizer tok = new QuotedStringTokenizer(string.substring(MetadataSerializationConfiguration.AUTOMATIC_PERSONS_GROUP_ID + .length()), MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + + String name = StringUtil.unquote(tok.nextToken(), MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + GroupHierarchyType context = GroupHierarchyType.getByNumberOrDefault(Integer.parseInt(tok.nextToken())); + String field = StringUtil.unquote(tok.nextToken(), MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + AutomaticPersonsGroup newGroup = new AutomaticPersonsGroup(name, context, field); + addGroupDetails(tok, newGroup); + return newGroup; + } + + private static AbstractGroup automaticKeywordGroupFromString(String string) { + if (!string.startsWith(MetadataSerializationConfiguration.AUTOMATIC_KEYWORD_GROUP_ID)) { + throw new IllegalArgumentException("KeywordGroup cannot be created from \"" + string + "\"."); + } + QuotedStringTokenizer tok = new QuotedStringTokenizer(string.substring(MetadataSerializationConfiguration.AUTOMATIC_KEYWORD_GROUP_ID + .length()), MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + + String name = StringUtil.unquote(tok.nextToken(), MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + GroupHierarchyType context = GroupHierarchyType.getByNumberOrDefault(Integer.parseInt(tok.nextToken())); + String field = StringUtil.unquote(tok.nextToken(), MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + Character separator = tok.nextToken().charAt(0); + AutomaticKeywordGroup newGroup = new AutomaticKeywordGroup(name, context, field, separator); + addGroupDetails(tok, newGroup); + return newGroup; } /** @@ -106,11 +149,14 @@ private static KeywordGroup keywordGroupFromString(String s, Character keywordSe String expression = StringUtil.unquote(tok.nextToken(), MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); boolean caseSensitive = Integer.parseInt(tok.nextToken()) == 1; boolean regExp = Integer.parseInt(tok.nextToken()) == 1; + KeywordGroup newGroup; if (regExp) { - return new RegexKeywordGroup(name, context, field, expression, caseSensitive); + newGroup = new RegexKeywordGroup(name, context, field, expression, caseSensitive); } else { - return new WordKeywordGroup(name, context, field, expression, caseSensitive, keywordSeparator, false); + newGroup = new WordKeywordGroup(name, context, field, expression, caseSensitive, keywordSeparator, false); } + addGroupDetails(tok, newGroup); + return newGroup; } private static ExplicitGroup explicitGroupFromString(String input, Character keywordSeparator) throws ParseException { @@ -120,6 +166,24 @@ private static ExplicitGroup explicitGroupFromString(String input, Character key QuotedStringTokenizer tok = new QuotedStringTokenizer(input.substring(MetadataSerializationConfiguration.EXPLICIT_GROUP_ID.length()), MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + String name = tok.nextToken(); + try { + int context = Integer.parseInt(tok.nextToken()); + ExplicitGroup newGroup = new ExplicitGroup(name, GroupHierarchyType.getByNumberOrDefault(context), keywordSeparator); + addGroupDetails(tok, newGroup); + return newGroup; + } catch (NumberFormatException exception) { + throw new ParseException("Could not parse context in " + input); + } + } + + private static ExplicitGroup legacyExplicitGroupFromString(String input, Character keywordSeparator) throws ParseException { + if (!input.startsWith(MetadataSerializationConfiguration.LEGACY_EXPLICIT_GROUP_ID)) { + throw new IllegalArgumentException("ExplicitGroup cannot be created from \"" + input + "\"."); + } + QuotedStringTokenizer tok = new QuotedStringTokenizer(input.substring(MetadataSerializationConfiguration.LEGACY_EXPLICIT_GROUP_ID.length()), + MetadataSerializationConfiguration.GROUP_UNIT_SEPARATOR, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR); + String name = tok.nextToken(); try { int context = Integer.parseInt(tok.nextToken()); @@ -175,4 +239,13 @@ private static AbstractGroup searchGroupFromString(String s) { GroupHierarchyType.getByNumberOrDefault(context), StringUtil.unquote(expression, MetadataSerializationConfiguration.GROUP_QUOTE_CHAR), caseSensitive, regExp ); } + + private static void addGroupDetails(QuotedStringTokenizer tokenizer, AbstractGroup group) { + if (tokenizer.hasMoreTokens()) { + group.setExpanded(Integer.parseInt(tokenizer.nextToken()) == 1); + group.setColor(tokenizer.nextToken()); + group.setIconCode(tokenizer.nextToken()); + group.setDescription(tokenizer.nextToken()); + } + } } diff --git a/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java b/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java index 22178238a58..d00ced1bdbb 100644 --- a/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java +++ b/src/main/java/org/jabref/logic/util/MetadataSerializationConfiguration.java @@ -1,6 +1,8 @@ package org.jabref.logic.util; import org.jabref.model.groups.AllEntriesGroup; +import org.jabref.model.groups.AutomaticKeywordGroup; +import org.jabref.model.groups.AutomaticPersonsGroup; import org.jabref.model.groups.ExplicitGroup; import org.jabref.model.groups.RegexKeywordGroup; import org.jabref.model.groups.SearchGroup; @@ -30,13 +32,29 @@ public class MetadataSerializationConfiguration { */ public static final String ALL_ENTRIES_GROUP_ID = "AllEntriesGroup:"; + /** + * Old identifier for {@link ExplicitGroup} (explicitly contained a list of {@link + * org.jabref.model.entry.BibEntry}). + */ + public static final String LEGACY_EXPLICIT_GROUP_ID = "ExplicitGroup:"; + /** * Identifier for {@link ExplicitGroup}. */ - public static final String EXPLICIT_GROUP_ID = "ExplicitGroup:"; + public static final String EXPLICIT_GROUP_ID = "StaticGroup:"; /** * Identifier for {@link SearchGroup}. */ public static final String SEARCH_GROUP_ID = "SearchGroup:"; + + /** + * Identifier for {@link AutomaticPersonsGroup}. + */ + public static final String AUTOMATIC_PERSONS_GROUP_ID = "AutomaticPersonsGroup:"; + + /** + * Identifier for {@link AutomaticKeywordGroup}. + */ + public static final String AUTOMATIC_KEYWORD_GROUP_ID = "AutomaticKeywordGroup:"; } diff --git a/src/main/java/org/jabref/logic/util/strings/QuotedStringTokenizer.java b/src/main/java/org/jabref/logic/util/strings/QuotedStringTokenizer.java index 373a84e4129..720d6fe12e2 100644 --- a/src/main/java/org/jabref/logic/util/strings/QuotedStringTokenizer.java +++ b/src/main/java/org/jabref/logic/util/strings/QuotedStringTokenizer.java @@ -47,9 +47,7 @@ public String nextToken() { } } else if (isDelimiter(c)) { // unit finished // advance index until next token or end - do { - ++index; - } while (index < contentLength && isDelimiter(content.charAt(index))); + ++index; return stringBuilder.toString(); } else { stringBuilder.append(c); diff --git a/src/main/java/org/jabref/model/groups/AbstractGroup.java b/src/main/java/org/jabref/model/groups/AbstractGroup.java index f8b5bd7ad56..259a7179a4c 100644 --- a/src/main/java/org/jabref/model/groups/AbstractGroup.java +++ b/src/main/java/org/jabref/model/groups/AbstractGroup.java @@ -8,6 +8,7 @@ import org.jabref.model.entry.BibEntry; import org.jabref.model.search.SearchMatcher; +import org.jabref.model.strings.StringUtil; /** * Base class for all groups. @@ -36,6 +37,14 @@ public Optional getColor() { return color; } + public void setColor(String colorString) { + if (StringUtil.isBlank(colorString)) { + color = Optional.empty(); + } else { + setColor(Color.valueOf(colorString)); + } + } + public void setColor(Color color) { this.color = Optional.of(color); } diff --git a/src/main/java/org/jabref/model/groups/AutomaticKeywordGroup.java b/src/main/java/org/jabref/model/groups/AutomaticKeywordGroup.java index 48641d24915..ea95b545770 100644 --- a/src/main/java/org/jabref/model/groups/AutomaticKeywordGroup.java +++ b/src/main/java/org/jabref/model/groups/AutomaticKeywordGroup.java @@ -1,5 +1,6 @@ package org.jabref.model.groups; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -32,6 +33,20 @@ public AbstractGroup deepCopy() { return new AutomaticKeywordGroup(this.name, this.context, field, this.keywordSeperator); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AutomaticKeywordGroup that = (AutomaticKeywordGroup) o; + return Objects.equals(keywordSeperator, that.keywordSeperator) && + Objects.equals(field, that.field); + } + + @Override + public int hashCode() { + return Objects.hash(keywordSeperator, field); + } + @Override public Set createSubgroups(BibEntry entry) { Optional keywordList = entry.getLatexFreeField(field) diff --git a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java index 5e4be74b20a..61992508ab9 100644 --- a/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java +++ b/src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java @@ -1,5 +1,6 @@ package org.jabref.model.groups; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -18,6 +19,19 @@ public AutomaticPersonsGroup(String name, GroupHierarchyType context, String fie this.field = field; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AutomaticPersonsGroup that = (AutomaticPersonsGroup) o; + return Objects.equals(field, that.field); + } + + @Override + public int hashCode() { + return Objects.hash(field); + } + @Override public AbstractGroup deepCopy() { return new AutomaticPersonsGroup(this.name, this.context, this.field); diff --git a/src/main/java/org/jabref/model/groups/ExplicitGroup.java b/src/main/java/org/jabref/model/groups/ExplicitGroup.java index ab3bb602ec3..9c6adaa23a6 100644 --- a/src/main/java/org/jabref/model/groups/ExplicitGroup.java +++ b/src/main/java/org/jabref/model/groups/ExplicitGroup.java @@ -45,8 +45,13 @@ public boolean equals(Object o) { return false; } ExplicitGroup other = (ExplicitGroup) o; - return Objects.equals(getName(), other.getName()) && Objects.equals(getHierarchicalContext(), - other.getHierarchicalContext()) && Objects.equals(getLegacyEntryKeys(), other.getLegacyEntryKeys()); + return Objects.equals(getName(), other.getName()) + && Objects.equals(getHierarchicalContext(), other.getHierarchicalContext()) + && Objects.equals(getIconCode(), other.getIconCode()) + && Objects.equals(getDescription(), other.getDescription()) + && Objects.equals(getColor(), other.getColor()) + && Objects.equals(isExpanded(), other.isExpanded()) + && Objects.equals(getLegacyEntryKeys(), other.getLegacyEntryKeys()); } public void clearLegacyEntryKeys() { @@ -59,7 +64,7 @@ public List getLegacyEntryKeys() { @Override public int hashCode() { - return Objects.hash(name, context, legacyEntryKeys); + return Objects.hash(name, context, legacyEntryKeys, iconCode, color, description, isExpanded); } @Override diff --git a/src/test/java/org/jabref/logic/exporter/BibtexDatabaseWriterTest.java b/src/test/java/org/jabref/logic/exporter/BibtexDatabaseWriterTest.java index 8ce78a579da..f9a5ebd70bb 100644 --- a/src/test/java/org/jabref/logic/exporter/BibtexDatabaseWriterTest.java +++ b/src/test/java/org/jabref/logic/exporter/BibtexDatabaseWriterTest.java @@ -196,7 +196,7 @@ public void writeGroups() throws Exception { assertEquals(OS.NEWLINE + "@Comment{jabref-meta: groupstree:" + OS.NEWLINE + "0 AllEntriesGroup:;" + OS.NEWLINE - + "1 ExplicitGroup:test\\;2\\;;" + OS.NEWLINE + + "1 StaticGroup:test\\;2\\;1\\;\\;\\;\\;;" + OS.NEWLINE + "}" + OS.NEWLINE, session.getStringValue()); // @formatter:on } @@ -217,7 +217,7 @@ public void writeGroupsAndEncoding() throws Exception { OS.NEWLINE + "@Comment{jabref-meta: groupstree:" + OS.NEWLINE + "0 AllEntriesGroup:;" + OS.NEWLINE - + "1 ExplicitGroup:test\\;2\\;;" + OS.NEWLINE + + "1 StaticGroup:test\\;2\\;1\\;\\;\\;\\;;" + OS.NEWLINE + "}" + OS.NEWLINE, session.getStringValue()); // @formatter:on } diff --git a/src/test/java/org/jabref/logic/exporter/GroupSerializerTest.java b/src/test/java/org/jabref/logic/exporter/GroupSerializerTest.java index 41bbe19da38..626d7e2b7c0 100644 --- a/src/test/java/org/jabref/logic/exporter/GroupSerializerTest.java +++ b/src/test/java/org/jabref/logic/exporter/GroupSerializerTest.java @@ -4,7 +4,12 @@ import java.util.Collections; import java.util.List; +import javafx.scene.paint.Color; + import org.jabref.model.groups.AllEntriesGroup; +import org.jabref.model.groups.AutomaticGroup; +import org.jabref.model.groups.AutomaticKeywordGroup; +import org.jabref.model.groups.AutomaticPersonsGroup; import org.jabref.model.groups.ExplicitGroup; import org.jabref.model.groups.GroupHierarchyType; import org.jabref.model.groups.GroupTreeNode; @@ -39,7 +44,18 @@ public void serializeSingleAllEntriesGroup() { public void serializeSingleExplicitGroup() { ExplicitGroup group = new ExplicitGroup("myExplicitGroup", GroupHierarchyType.INDEPENDENT, ','); List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); - assertEquals(Collections.singletonList("0 ExplicitGroup:myExplicitGroup;0;"), serialization); + assertEquals(Collections.singletonList("0 StaticGroup:myExplicitGroup;0;1;;;;"), serialization); + } + + @Test + public void serializeSingleExplicitGroupWithIconAndDescription() { + ExplicitGroup group = new ExplicitGroup("myExplicitGroup", GroupHierarchyType.INDEPENDENT, ','); + group.setIconCode("test icon"); + group.setExpanded(true); + group.setColor(Color.ALICEBLUE); + group.setDescription("test description"); + List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); + assertEquals(Collections.singletonList("0 StaticGroup:myExplicitGroup;0;1;0xf0f8ffff;test icon;test description;"), serialization); } @Test @@ -47,35 +63,49 @@ public void serializeSingleExplicitGroup() { public void serializeSingleExplicitGroupWithEscapedSlash() { ExplicitGroup group = new ExplicitGroup("B{\\\"{o}}hmer", GroupHierarchyType.INDEPENDENT, ','); List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); - assertEquals(Collections.singletonList("0 ExplicitGroup:B{\\\\\"{o}}hmer;0;"), serialization); + assertEquals(Collections.singletonList("0 StaticGroup:B{\\\\\"{o}}hmer;0;1;;;;"), serialization); } @Test public void serializeSingleSimpleKeywordGroup() { WordKeywordGroup group = new WordKeywordGroup("name", GroupHierarchyType.INDEPENDENT, "keywords", "test", false, ',', false); List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); - assertEquals(Collections.singletonList("0 KeywordGroup:name;0;keywords;test;0;0;"), serialization); + assertEquals(Collections.singletonList("0 KeywordGroup:name;0;keywords;test;0;0;1;;;;"), serialization); } @Test public void serializeSingleRegexKeywordGroup() { KeywordGroup group = new RegexKeywordGroup("myExplicitGroup", GroupHierarchyType.REFINING, "author", "asdf", false); List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); - assertEquals(Collections.singletonList("0 KeywordGroup:myExplicitGroup;1;author;asdf;0;1;"), serialization); + assertEquals(Collections.singletonList("0 KeywordGroup:myExplicitGroup;1;author;asdf;0;1;1;;;;"), serialization); } @Test public void serializeSingleSearchGroup() { SearchGroup group = new SearchGroup("myExplicitGroup", GroupHierarchyType.INDEPENDENT, "author=harrer", true, true); List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); - assertEquals(Collections.singletonList("0 SearchGroup:myExplicitGroup;0;author=harrer;1;1;"), serialization); + assertEquals(Collections.singletonList("0 SearchGroup:myExplicitGroup;0;author=harrer;1;1;1;;;;"), serialization); } @Test public void serializeSingleSearchGroupWithRegex() { SearchGroup group = new SearchGroup("myExplicitGroup", GroupHierarchyType.INCLUDING, "author=\"harrer\"", true, false); List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); - assertEquals(Collections.singletonList("0 SearchGroup:myExplicitGroup;2;author=\"harrer\";1;0;"), serialization); + assertEquals(Collections.singletonList("0 SearchGroup:myExplicitGroup;2;author=\"harrer\";1;0;1;;;;"), serialization); + } + + @Test + public void serializeSingleAutomaticKeywordGroup() { + AutomaticGroup group = new AutomaticKeywordGroup("myAutomaticGroup", GroupHierarchyType.INDEPENDENT, "keywords", ','); + List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); + assertEquals(Collections.singletonList("0 AutomaticKeywordGroup:myAutomaticGroup;0;keywords;,;1;;;;"), serialization); + } + + @Test + public void serializeSingleAutomaticPersonGroup() { + AutomaticPersonsGroup group = new AutomaticPersonsGroup("myAutomaticGroup", GroupHierarchyType.INDEPENDENT, "authors"); + List serialization = groupSerializer.serializeTree(GroupTreeNode.fromGroup(group)); + assertEquals(Collections.singletonList("0 AutomaticPersonsGroup:myAutomaticGroup;0;authors;1;;;;"), serialization); } @Test @@ -85,9 +115,9 @@ public void getTreeAsStringInSimpleTree() throws Exception { List expected = Arrays.asList( "0 AllEntriesGroup:", - "1 ExplicitGroup:ExplicitA;2;", - "1 ExplicitGroup:ExplicitParent;0;", - "2 ExplicitGroup:ExplicitNode;1;" + "1 StaticGroup:ExplicitA;2;1;;;;", + "1 StaticGroup:ExplicitParent;0;1;;;;", + "2 StaticGroup:ExplicitNode;1;1;;;;" ); assertEquals(expected, groupSerializer.serializeTree(root)); } @@ -99,19 +129,19 @@ public void getTreeAsStringInComplexTree() throws Exception { List expected = Arrays.asList( "0 AllEntriesGroup:", - "1 SearchGroup:SearchA;2;searchExpression;1;0;", - "1 ExplicitGroup:ExplicitA;2;", - "1 ExplicitGroup:ExplicitGrandParent;0;", - "2 ExplicitGroup:ExplicitB;1;", - "2 KeywordGroup:KeywordParent;0;searchField;searchExpression;1;0;", - "3 KeywordGroup:KeywordNode;0;searchField;searchExpression;1;0;", - "4 ExplicitGroup:ExplicitChild;1;", - "3 SearchGroup:SearchC;2;searchExpression;1;0;", - "3 ExplicitGroup:ExplicitC;1;", - "3 KeywordGroup:KeywordC;0;searchField;searchExpression;1;0;", - "2 SearchGroup:SearchB;2;searchExpression;1;0;", - "2 KeywordGroup:KeywordB;0;searchField;searchExpression;1;0;", - "1 KeywordGroup:KeywordA;0;searchField;searchExpression;1;0;" + "1 SearchGroup:SearchA;2;searchExpression;1;0;1;;;;", + "1 StaticGroup:ExplicitA;2;1;;;;", + "1 StaticGroup:ExplicitGrandParent;0;1;;;;", + "2 StaticGroup:ExplicitB;1;1;;;;", + "2 KeywordGroup:KeywordParent;0;searchField;searchExpression;1;0;1;;;;", + "3 KeywordGroup:KeywordNode;0;searchField;searchExpression;1;0;1;;;;", + "4 StaticGroup:ExplicitChild;1;1;;;;", + "3 SearchGroup:SearchC;2;searchExpression;1;0;1;;;;", + "3 StaticGroup:ExplicitC;1;1;;;;", + "3 KeywordGroup:KeywordC;0;searchField;searchExpression;1;0;1;;;;", + "2 SearchGroup:SearchB;2;searchExpression;1;0;1;;;;", + "2 KeywordGroup:KeywordB;0;searchField;searchExpression;1;0;1;;;;", + "1 KeywordGroup:KeywordA;0;searchField;searchExpression;1;0;1;;;;" ); assertEquals(expected, groupSerializer.serializeTree(root)); } diff --git a/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java b/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java index e74a171a30a..4e69fb5d223 100644 --- a/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java +++ b/src/test/java/org/jabref/logic/importer/util/GroupsParserTest.java @@ -3,8 +3,13 @@ import java.util.Arrays; import java.util.List; +import javafx.scene.paint.Color; + import org.jabref.logic.importer.ParseException; import org.jabref.model.groups.AbstractGroup; +import org.jabref.model.groups.AutomaticGroup; +import org.jabref.model.groups.AutomaticKeywordGroup; +import org.jabref.model.groups.AutomaticPersonsGroup; import org.jabref.model.groups.ExplicitGroup; import org.jabref.model.groups.GroupHierarchyType; import org.jabref.model.groups.GroupTreeNode; @@ -33,7 +38,7 @@ public void fromStringThrowsParseExceptionForNotEscapedGroupName() throws Except public void testImportSubGroups() throws ParseException { List orderedData = Arrays.asList("0 AllEntriesGroup:", "1 ExplicitGroup:1;0;", - "2 ExplicitGroup:2;0;;", "0 ExplicitGroup:3;0;;"); + "2 ExplicitGroup:2;0;", "0 ExplicitGroup:3;0;"); //Create group hierarchy: // Level 0 Name: All entries // Level 1 Name: 1 @@ -57,4 +62,34 @@ public void testImportSubGroups() throws ParseException { } + @Test + public void fromStringParsesExplicitGroupWithIconAndDesrcitpion() throws Exception { + ExplicitGroup expected = new ExplicitGroup("myExplicitGroup", GroupHierarchyType.INDEPENDENT, ','); + expected.setIconCode("test icon"); + expected.setExpanded(true); + expected.setColor(Color.ALICEBLUE); + expected.setDescription("test description"); + AbstractGroup parsed = GroupsParser.fromString("StaticGroup:myExplicitGroup;0;1;0xf0f8ffff;test icon;test description;", ','); + + assertEquals(expected, parsed); + } + + @Test + public void fromStringParsesAutomaticKeywordGroup() throws Exception { + AutomaticGroup expected = new AutomaticKeywordGroup("myAutomaticGroup", GroupHierarchyType.INDEPENDENT, "keywords", ','); + AbstractGroup parsed = GroupsParser.fromString("AutomaticKeywordGroup:myAutomaticGroup;0;keywords;,;1;;;;", ','); + assertEquals(expected, parsed); + } + + @Test + public void fromStringParsesAutomaticPersonGroup() throws Exception { + AutomaticPersonsGroup expected = new AutomaticPersonsGroup("myAutomaticGroup", GroupHierarchyType.INDEPENDENT, "authors"); + AbstractGroup parsed = GroupsParser.fromString("AutomaticPersonsGroup:myAutomaticGroup;0;authors;1;;;;", ','); + assertEquals(expected, parsed); + } + + @Test(expected = ParseException.class) + public void fromStringUnknownGroupThrowsException() throws Exception { + GroupsParser.fromString("0 UnknownGroup:myUnknownGroup;0;;1;;;;", ','); + } } diff --git a/src/test/resources/testbib/complex.bib b/src/test/resources/testbib/complex.bib index 769c1962b32..e8a50a4b43f 100644 --- a/src/test/resources/testbib/complex.bib +++ b/src/test/resources/testbib/complex.bib @@ -24,21 +24,20 @@ @ARTICLE{1102917 publisher = {Springer-Verlag} } -@INPROCEEDINGS{1137631, - author = {Gustav Bostr\"{o}m and Jaana W\"{a}yrynen and Marine Bod\'{e}n and - Konstantin Beznosov and Philippe Kruchten}, - title = {Extending XP practices to support security requirements engineering}, - booktitle = {SESS '06: Proceedings of the 2006 international workshop on Software - engineering for secure systems}, - year = {2006}, - pages = {11--18}, - address = {New York, NY, USA}, - publisher = {ACM}, +@InProceedings{1137631, + author = {Gustav Bostr\"{o}m and Jaana W\"{a}yrynen and Marine Bod\'{e}n and Konstantin Beznosov and Philippe Kruchten}, + title = {Extending XP practices to support security requirements engineering}, + booktitle = {SESS '06: Proceedings of the 2006 international workshop on Software engineering for secure systems}, + year = {2006}, + publisher = {ACM}, + location = {Shanghai, China}, + isbn = {1-59593-411-1}, + pages = {11--18}, + doi = {http://doi.acm.org/10.1145/1137627.1137631}, + address = {New York, NY, USA}, bdsk-url-1 = {http://doi.acm.org/10.1145/1137627.1137631}, - doi = {http://doi.acm.org/10.1145/1137627.1137631}, - file = {:/Volumes/iDisk/Freie Universität Berlin/Semester 9/Softwareprozesse/p11-bostrom.pdf:PDF}, - isbn = {1-59593-411-1}, - location = {Shanghai, China} + file = {:/Volumes/iDisk/Freie Universität Berlin/Semester 9/Softwareprozesse/p11-bostrom.pdf:PDF}, + groups = {StaticGroup}, } @INPROCEEDINGS{1132768, @@ -126,19 +125,19 @@ @INPROCEEDINGS{1143122 location = {Pittsburgh, Pennsylvania} } -@INPROCEEDINGS{1233448, - author = {Cheryl Hinds and Chinedu Ekwueme}, - title = {Increasing security and usability of computer systems with graphical - passwords}, - booktitle = {ACM-SE 45: Proceedings of the 45th annual southeast regional conference}, - year = {2007}, - pages = {529--530}, - address = {New York, NY, USA}, - publisher = {ACM}, +@InProceedings{1233448, + author = {Cheryl Hinds and Chinedu Ekwueme}, + title = {Increasing security and usability of computer systems with graphical passwords}, + booktitle = {ACM-SE 45: Proceedings of the 45th annual southeast regional conference}, + year = {2007}, + publisher = {ACM}, + location = {Winston-Salem, North Carolina}, + isbn = {978-1-59593-629-5}, + pages = {529--530}, + doi = {http://doi.acm.org/10.1145/1233341.1233448}, + address = {New York, NY, USA}, bdsk-url-1 = {http://doi.acm.org/10.1145/1233341.1233448}, - doi = {http://doi.acm.org/10.1145/1233341.1233448}, - isbn = {978-1-59593-629-5}, - location = {Winston-Salem, North Carolina} + groups = {StaticGroup}, } @ARTICLE{10250999, @@ -156,19 +155,19 @@ @ARTICLE{10250999 publisher = {IEEE Educational Activities Department} } -@INPROCEEDINGS{1314293, - author = {Mariusz H. Jakubowski and Ramarathnam Venkatesan}, - title = {Randomized radon transforms for biometric authentication via fingerprint - hashing}, - booktitle = {DRM '07: Proceedings of the 2007 ACM workshop on Digital Rights Management}, - year = {2007}, - pages = {90--94}, - address = {New York, NY, USA}, - publisher = {ACM}, +@InProceedings{1314293, + author = {Mariusz H. Jakubowski and Ramarathnam Venkatesan}, + title = {Randomized radon transforms for biometric authentication via fingerprint hashing}, + booktitle = {DRM '07: Proceedings of the 2007 ACM workshop on Digital Rights Management}, + year = {2007}, + publisher = {ACM}, + location = {Alexandria, Virginia, USA}, + isbn = {978-1-59593-884-8}, + pages = {90--94}, + doi = {http://doi.acm.org/10.1145/1314276.1314293}, + address = {New York, NY, USA}, bdsk-url-1 = {http://doi.acm.org/10.1145/1314276.1314293}, - doi = {http://doi.acm.org/10.1145/1314276.1314293}, - isbn = {978-1-59593-884-8}, - location = {Alexandria, Virginia, USA} + groups = {StaticGroup}, } @INPROCEEDINGS{1358810, @@ -292,8 +291,9 @@ @Comment{jabref-meta: @Comment{jabref-meta: groupstree: 0 AllEntriesGroup:; -1 ExplicitGroup:StaticGroup\;0\;1137631\;1233448\;1314293\;; -1 KeywordGroup:DynamicGroup\;0\;author\;Churchill\;0\;0\;; +1 StaticGroup:StaticGroup\;0\;1\;\;\;A test static group\;; +1 KeywordGroup:DynamicGroup\;0\;author\;Churchill\;0\;0\;1\;\;\;\;; +1 AutomaticPersonsGroup:Automatic group (Authors)\;0\;author\;1\;\;\;Automatic group for all authors\;; } @Comment{jabref-meta: keypattern_article:articleTest;}