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

[WIP] Enhance customization of entry types #11230

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
16 changes: 16 additions & 0 deletions src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,20 @@ public static String serializeCustomEntryTypes(BibEntryType entryType) {
builder.append("]");
return builder.toString();
}

public static String serializeCustomEntryTypesV2(BibEntryType entryType) {
StringBuilder builder = new StringBuilder();
builder.append(MetaData.ENTRYTYPE_FLAG_V2);
builder.append(entryType.getType().getName());
builder.append(": req[");
builder.append(FieldFactory.serializeOrFieldsListV2(entryType.getRequiredFields()));
builder.append("] opt[");
builder.append(FieldFactory.serializeFieldsListV2(
entryType.getOptionalFields()
.stream()
.map(BibField::field)
.collect(Collectors.toList())));
builder.append("]");
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public MetaDataParser(FileUpdateMonitor fileMonitor) {

public static Optional<BibEntryType> parseCustomEntryType(String comment) {
String rest = comment.substring(MetaData.ENTRYTYPE_FLAG.length());
if (comment.startsWith(MetaData.ENTRYTYPE_FLAG_V2)) {
rest = comment.substring(MetaData.ENTRYTYPE_FLAG_V2.length());
}
int indexEndOfName = rest.indexOf(':');
if (indexEndOfName < 0) {
return Optional.empty();
Expand Down
74 changes: 74 additions & 0 deletions src/main/java/org/jabref/model/entry/field/FieldFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.jabref.model.entry.types.EntryType;
Expand All @@ -27,6 +28,8 @@ public class FieldFactory {
*/
private static final String FIELD_OR_SEPARATOR = "/";
private static final String DELIMITER = ";";
private static final String FIELD_PROPERTY_SEPARATOR = ",";
private static final String FIELD_NAME_PROPERTY_SEPARATOR = "|";

public static String serializeOrFields(Field... fields) {
return serializeOrFields(new OrFields(fields));
Expand All @@ -46,10 +49,45 @@ public static String serializeOrFields(OrFields fields) {
.collect(Collectors.joining(FIELD_OR_SEPARATOR));
}

public static String serializeOrFieldsV2(OrFields fields) {
return fields.getFields().stream()
.map(field -> {
if (field instanceof UnknownField unknownField) {
return serializeUnknownField(unknownField);
} else {
// In all fields known to JabRef, the name is used - JabRef knows better than the user how to case the field
return field.getName();
}
})
.collect(Collectors.joining(FIELD_OR_SEPARATOR));
}

private static String serializeUnknownField(UnknownField unknownField) {
// In case a user has put a user-defined field, the casing of that field is kept
String displayName = unknownField.getDisplayName();
String fieldProperties = unknownField.getProperties().stream()
.map(Enum::name)
.collect(Collectors.joining(FIELD_PROPERTY_SEPARATOR));

if (fieldProperties.isBlank()) {
return displayName;
}

return displayName + FIELD_NAME_PROPERTY_SEPARATOR + fieldProperties;
}

public static String serializeOrFieldsList(Set<OrFields> fields) {
return fields.stream().map(FieldFactory::serializeOrFields).collect(Collectors.joining(DELIMITER));
}

public static String serializeOrFieldsListV2(Set<OrFields> fields) {
return fields.stream().map(FieldFactory::serializeOrFieldsV2).collect(Collectors.joining(DELIMITER));
}

public static List<Field> getNotTextFieldNames() {
return Arrays.asList(StandardField.DOI, StandardField.FILE, StandardField.URL, StandardField.URI, StandardField.ISBN, StandardField.ISSN, StandardField.MONTH, StandardField.DATE, StandardField.YEAR);
}

/**
* Checks whether the given field contains LaTeX code or something else
*/
Expand Down Expand Up @@ -107,6 +145,19 @@ public static String serializeFieldsList(Collection<Field> fields) {
.collect(Collectors.joining(DELIMITER));
}

public static String serializeFieldsListV2(Collection<Field> fields) {
return fields.stream()
.map(field -> {
if (field instanceof UnknownField unknownField) {
return serializeUnknownField(unknownField);
} else {
// In all fields known to JabRef, the name is used - JabRef knows better than the user how to case the field
return field.getName();
}
})
.collect(Collectors.joining(DELIMITER));
}

/**
* Type T is an entry type and is used to direct the mapping to the Java field class.
* This somehow acts as filter, BibLaTeX "APA" entry type has field "article", but we want to have StandardField (if not explicitly requested otherwise)
Expand All @@ -117,6 +168,29 @@ public static <T extends EntryType> Field parseField(T type, String fieldName) {
String username = fieldName.substring("comment-".length());
return new UserSpecificCommentField(username);
}

if (fieldName.contains(FIELD_NAME_PROPERTY_SEPARATOR)) {
String[] components = fieldName.split(Pattern.quote(FIELD_NAME_PROPERTY_SEPARATOR));

if (components.length == 2) {
String unknownFieldName = components[0];
String[] fieldProperties = components[1].split(Pattern.quote(FIELD_PROPERTY_SEPARATOR));

if (fieldProperties.length == 0) {
return UnknownField.fromDisplayName(unknownFieldName);
} else if (fieldProperties.length == 1) {
return new UnknownField(unknownFieldName, unknownFieldName, FieldProperty.valueOf(fieldProperties[0]));
} else {
FieldProperty firstProperty = FieldProperty.valueOf(fieldProperties[0]);
FieldProperty[] restProperties = Arrays.stream(fieldProperties, 1, fieldProperties.length)
.map(FieldProperty::valueOf)
.toArray(FieldProperty[]::new);

return new UnknownField(unknownFieldName, unknownFieldName, firstProperty, restProperties);
}
}
}

return OptionalUtil.<Field>orElse(
OptionalUtil.<Field>orElse(
OptionalUtil.<Field>orElse(
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/jabref/model/metadata/MetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class MetaData {

public static final String META_FLAG = "jabref-meta: ";
public static final String ENTRYTYPE_FLAG = "jabref-entrytype: ";
public static final String ENTRYTYPE_FLAG_V2 = "v2-jabref-entrytype: ";
public static final String SAVE_ORDER_CONFIG = "saveOrderConfig"; // ToDo: Rename in next major version to saveOrder, adapt testbibs
public static final String SAVE_ACTIONS = "saveActions";
public static final String PREFIX_KEYPATTERN = "keypattern_";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.jabref.model.entry.BibEntryTypeBuilder;
import org.jabref.model.entry.field.BibField;
import org.jabref.model.entry.field.FieldPriority;
import org.jabref.model.entry.field.FieldProperty;
import org.jabref.model.entry.field.OrFields;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.field.UnknownField;
Expand Down Expand Up @@ -140,4 +141,62 @@ public static Stream<Arguments> serializeCustomizedEntryType() {
void serializeCustomizedEntryType(BibEntryTypeBuilder bibEntryTypeBuilder, String expected) {
assertEquals(expected, MetaDataSerializer.serializeCustomEntryTypes(bibEntryTypeBuilder.build()));
}

public static Stream<Arguments> serializeCustomizedEntryTypeV2() {
return Stream.of(
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(StandardField.AUTHOR, StandardField.TITLE),
"v2-jabref-entrytype: test: req[author;title] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(StandardField.AUTHOR)
.withImportantFields(StandardField.TITLE),
"v2-jabref-entrytype: test: req[author] opt[title]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(UnknownField.fromDisplayName("Test1"), UnknownField.fromDisplayName("Test2")),
"v2-jabref-entrytype: test: req[Test1;Test2] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(UnknownField.fromDisplayName("tEST"), UnknownField.fromDisplayName("tEsT2")),
"v2-jabref-entrytype: test: req[tEST;tEsT2] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("person"))
.withRequiredFields(new UnknownField("Name", FieldProperty.PERSON_NAMES))
.withImportantFields(
new UnknownField("Googlescholar", FieldProperty.EXTERNAL),
new UnknownField("Orcid", FieldProperty.EXTERNAL)
),
"v2-jabref-entrytype: person: req[Name|PERSON_NAMES] opt[Googlescholar|EXTERNAL;Orcid|EXTERNAL]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(new UnknownField("custom1", "custom1", FieldProperty.MULTILINE_TEXT, FieldProperty.EXTERNAL)),
"v2-jabref-entrytype: test: req[custom1|EXTERNAL,MULTILINE_TEXT] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(new UnknownField("custom2", "custom2")),
"v2-jabref-entrytype: test: req[custom2] opt[]"
)
);
}

@ParameterizedTest
@MethodSource
void serializeCustomizedEntryTypeV2(BibEntryTypeBuilder bibEntryTypeBuilder, String expected) {
assertEquals(expected, MetaDataSerializer.serializeCustomEntryTypesV2(bibEntryTypeBuilder.build()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.jabref.logic.exporter.MetaDataSerializerTest;
import org.jabref.logic.formatter.casechanger.LowerCaseFormatter;
import org.jabref.model.entry.BibEntryTypeBuilder;
import org.jabref.model.entry.field.FieldProperty;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.field.UnknownField;
import org.jabref.model.entry.types.UnknownEntryType;
Expand Down Expand Up @@ -56,6 +57,53 @@ public static Stream<Arguments> parseCustomizedEntryType() {
.withType(new UnknownEntryType("test"))
.withRequiredFields(UnknownField.fromDisplayName("tEST"), UnknownField.fromDisplayName("tEsT2")),
"jabref-entrytype: test: req[tEST;tEsT2] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(StandardField.AUTHOR, StandardField.TITLE),
"v2-jabref-entrytype: test: req[author;title] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(StandardField.AUTHOR)
.withImportantFields(StandardField.TITLE),
"v2-jabref-entrytype: test: req[author] opt[title]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(UnknownField.fromDisplayName("Test1"), UnknownField.fromDisplayName("Test2")),
"v2-jabref-entrytype: test: req[Test1;Test2] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(UnknownField.fromDisplayName("tEST"), UnknownField.fromDisplayName("tEsT2")),
"v2-jabref-entrytype: test: req[tEST;tEsT2] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("person"))
.withRequiredFields(new UnknownField("Name", FieldProperty.PERSON_NAMES))
.withImportantFields(
new UnknownField("Googlescholar", FieldProperty.EXTERNAL),
new UnknownField("Orcid", FieldProperty.EXTERNAL)
),
"v2-jabref-entrytype: person: req[Name|PERSON_NAMES] opt[Googlescholar|EXTERNAL;Orcid|EXTERNAL]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(new UnknownField("custom1", "custom1", FieldProperty.MULTILINE_TEXT, FieldProperty.EXTERNAL)),
"v2-jabref-entrytype: test: req[custom1|MULTILINE_TEXT,EXTERNAL] opt[]"
),
Arguments.of(
new BibEntryTypeBuilder()
.withType(new UnknownEntryType("test"))
.withRequiredFields(new UnknownField("custom2", "custom2")),
"v2-jabref-entrytype: test: req[custom2] opt[]"
)
);
}
Expand Down
Loading