diff --git a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java index 0099aff7..e4e50218 100644 --- a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java +++ b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsGenerator.java @@ -144,6 +144,9 @@ public void setSchema(FormatSchema schema) { _basePath.append(indent); _jpropContext = JPropWriteContext.createRootContext(_indentLength); } + if (_schema.prefix() != null) { + _basePath.append(_schema.prefix()); + } } return; } diff --git a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsSchema.java b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsSchema.java index fda518df..4d2bc58f 100644 --- a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsSchema.java +++ b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/JavaPropsSchema.java @@ -139,6 +139,12 @@ public class JavaPropsSchema */ protected String _header = ""; + /** + * Optional prefix to strip and append to key names. + * Useful when subset of properties need to be processed. + */ + protected String _prefix; + /* /********************************************************************** /* Construction, factories, mutant factories @@ -157,6 +163,7 @@ public JavaPropsSchema(JavaPropsSchema base) { _keyValueSeparator = base._keyValueSeparator; _lineEnding = base._lineEnding; _header = base._header; + _prefix = base._prefix; } /** @@ -287,6 +294,15 @@ public JavaPropsSchema withLineEnding(String v) { return s; } + public JavaPropsSchema withPrefix(String v) { + if (_equals(v, _prefix)) { + return this; + } + JavaPropsSchema s = new JavaPropsSchema(this); + s._prefix = v; + return s; + } + /** * Mutant factory for constructing schema instance where specified * header section (piece of text written out right before actual @@ -371,6 +387,10 @@ public String pathSeparator() { return _pathSeparator; } + public String prefix() { + return _prefix; + } + public boolean writeIndexUsingMarkers() { return _writeIndexUsingMarkers && (_indexMarker != null); } diff --git a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/util/JPropPathSplitter.java b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/util/JPropPathSplitter.java index 549a82c6..6d00c5e4 100644 --- a/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/util/JPropPathSplitter.java +++ b/properties/src/main/java/com/fasterxml/jackson/dataformat/javaprop/util/JPropPathSplitter.java @@ -35,7 +35,8 @@ public static JPropPathSplitter create(JavaPropsSchema schema) } return new FullSplitter(sep, schema.parseSimpleIndexes(), indexMarker, - pathOnlySplitter(schema)); + pathOnlySplitter(schema), + schema.prefix()); } private static JPropPathSplitter pathOnlySplitter(JavaPropsSchema schema) @@ -262,14 +263,21 @@ public static class FullSplitter extends JPropPathSplitter // small but important optimization for cases where index markers are absent protected final int _indexFirstChar; protected final JPropPathSplitter _simpleSplitter; - + protected final String _prefix; + public FullSplitter(String pathSeparator, boolean useSimpleIndex, - Markers indexMarker, JPropPathSplitter fallbackSplitter) + Markers indexMarker, JPropPathSplitter fallbackSplitter, + String prefix) { super(useSimpleIndex); String startMarker = indexMarker.getStart(); _indexFirstChar = startMarker.charAt(0); _simpleSplitter = fallbackSplitter; + if (prefix == null || prefix.isEmpty()) { + _prefix = null; + } else { + _prefix = prefix + pathSeparator; + } _indexMatch = Pattern.compile(String.format ("(%s)|(%s(\\d{1,9})%s)", Pattern.quote(pathSeparator), @@ -281,6 +289,13 @@ public FullSplitter(String pathSeparator, boolean useSimpleIndex, public JPropNode splitAndAdd(JPropNode parent, String key, String value) { + if (_prefix != null) { + if (!key.startsWith(_prefix)) { + return null; + } + key = key.substring(_prefix.length()); + } + if (key.indexOf(_indexFirstChar) < 0) { // no index start marker return _simpleSplitter.splitAndAdd(parent, key, value); } diff --git a/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/PrefixTest.java b/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/PrefixTest.java new file mode 100644 index 00000000..d2b957d1 --- /dev/null +++ b/properties/src/test/java/com/fasterxml/jackson/dataformat/javaprop/PrefixTest.java @@ -0,0 +1,42 @@ +package com.fasterxml.jackson.dataformat.javaprop; + +import java.util.Map; +import java.util.Properties; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class PrefixTest extends ModuleTestBase +{ + private final JavaPropsMapper MAPPER = mapperForProps(); + + public void testPrefixParsing() throws Exception { + final String INPUT = "org.o1.firstName=Bob\n" + +"org.o1.lastName=Palmer\n" + +"org.o2.firstName=Alice\n" + +"org.o2.lastName=Black\n" + +"junk=AQIDBA==\n"; + FiveMinuteUser result1 = _mapFrom(MAPPER.reader(JavaPropsSchema.emptySchema().withPrefix("org.o1")), INPUT, FiveMinuteUser.class, false); + assertEquals("Bob", result1.firstName); + assertEquals("Palmer", result1.lastName); + FiveMinuteUser result2 = _mapFrom(MAPPER.reader(JavaPropsSchema.emptySchema().withPrefix("org.o2")), INPUT, FiveMinuteUser.class, false); + assertEquals("Alice", result2.firstName); + assertEquals("Black", result2.lastName); + } + + public void testPrefixGeneration() throws Exception + { + FiveMinuteUser input = new FiveMinuteUser("Bob", "Palmer", true, Gender.MALE, + new byte[] { 1, 2, 3, 4 }); + String output = MAPPER.writer(JavaPropsSchema.emptySchema().withPrefix("org.o1")).writeValueAsString(input); + assertEquals("org.o1.firstName=Bob\n" + +"org.o1.lastName=Palmer\n" + +"org.o1.gender=MALE\n" + +"org.o1.verified=true\n" + +"org.o1.userImage=AQIDBA==\n" + ,output); + Properties props = MAPPER.writeValueAsProperties(input, JavaPropsSchema.emptySchema().withPrefix("org.o1")); + assertEquals(5, props.size()); + assertEquals("true", props.get("org.o1.verified")); + assertEquals("MALE", props.get("org.o1.gender")); + } +}