Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finally write the new group properties to the bib file: icon with color, description, expanded status and automatic groups #2634

Merged
merged 7 commits into from
Mar 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 55 additions & 9 deletions src/main/java/org/jabref/logic/exporter/GroupSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String> 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();
}

Expand All @@ -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();
}

Expand All @@ -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
Expand Down Expand Up @@ -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();
}
}
79 changes: 76 additions & 3 deletions src/main/java/org/jabref/logic/importer/util/GroupsParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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 {
Expand All @@ -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());
Expand Down Expand Up @@ -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());
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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:";
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/org/jabref/model/groups/AbstractGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -36,6 +37,14 @@ public Optional<Color> 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);
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/org/jabref/model/groups/AutomaticKeywordGroup.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<GroupTreeNode> createSubgroups(BibEntry entry) {
Optional<KeywordList> keywordList = entry.getLatexFreeField(field)
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/jabref/model/groups/AutomaticPersonsGroup.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/org/jabref/model/groups/ExplicitGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -59,7 +64,7 @@ public List<String> getLegacyEntryKeys() {

@Override
public int hashCode() {
return Objects.hash(name, context, legacyEntryKeys);
return Objects.hash(name, context, legacyEntryKeys, iconCode, color, description, isExpanded);
}

@Override
Expand Down
Loading