Browse Source

Introduce more robust parser replacement for PropertyLoader.

pull/235/head
weisj 4 years ago
parent
commit
969d8a9261
No known key found for this signature in database
GPG Key ID: 31124CB75461DA2A
  1. 38
      property-loader/src/main/java/com/github/weisj/darklaf/parser/ActiveObjectParser.java
  2. 38
      property-loader/src/main/java/com/github/weisj/darklaf/parser/Delimiters.java
  3. 41
      property-loader/src/main/java/com/github/weisj/darklaf/parser/DimensionParser.java
  4. 41
      property-loader/src/main/java/com/github/weisj/darklaf/parser/FallbackParser.java
  5. 101
      property-loader/src/main/java/com/github/weisj/darklaf/parser/FontParser.java
  6. 102
      property-loader/src/main/java/com/github/weisj/darklaf/parser/IconParser.java
  7. 43
      property-loader/src/main/java/com/github/weisj/darklaf/parser/InsetParser.java
  8. 37
      property-loader/src/main/java/com/github/weisj/darklaf/parser/JoinedParser.java
  9. 38
      property-loader/src/main/java/com/github/weisj/darklaf/parser/KeyFilteredParser.java
  10. 37
      property-loader/src/main/java/com/github/weisj/darklaf/parser/LazyObjectParser.java
  11. 34
      property-loader/src/main/java/com/github/weisj/darklaf/parser/ListParser.java
  12. 54
      property-loader/src/main/java/com/github/weisj/darklaf/parser/MapParser.java
  13. 35
      property-loader/src/main/java/com/github/weisj/darklaf/parser/NullParser.java
  14. 64
      property-loader/src/main/java/com/github/weisj/darklaf/parser/ParseResult.java
  15. 88
      property-loader/src/main/java/com/github/weisj/darklaf/parser/Parser.java
  16. 39
      property-loader/src/main/java/com/github/weisj/darklaf/parser/ParserContext.java
  17. 230
      property-loader/src/main/java/com/github/weisj/darklaf/parser/ParserUtil.java
  18. 58
      property-loader/src/main/java/com/github/weisj/darklaf/parser/PrimitiveParser.java
  19. 58
      property-loader/src/main/java/com/github/weisj/darklaf/parser/PropertyParser.java
  20. 46
      property-loader/src/main/java/com/github/weisj/darklaf/parser/ReferenceParser.java
  21. 213
      property-loader/src/test/java/com/github/weisj/darklaf/parser/ParserTest.java
  22. 10
      utils/src/main/java/com/github/weisj/darklaf/color/ColorUtil.java

38
property-loader/src/main/java/com/github/weisj/darklaf/parser/ActiveObjectParser.java

@ -0,0 +1,38 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import javax.swing.UIDefaults;
public class ActiveObjectParser extends KeyFilteredParser {
public ActiveObjectParser() {
super("Component", ".component");
}
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
return ParserUtil.setNonNull(parseResult,
(UIDefaults.ActiveValue) (def) -> ParserUtil.createObject(parseResult.value));
}
}

38
property-loader/src/main/java/com/github/weisj/darklaf/parser/Delimiters.java

@ -0,0 +1,38 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
public interface Delimiters {
char LIST_START = '[';
char LIST_END = ']';
char LIST_SEPARATOR = ',';
char MAP_START = '{';
char MAP_END = '}';
char MAP_SEPARATOR = ',';
char ARG_START = '(';
char ARG_END = ')';
char ARG_SEPARATOR = ',';
char PAIR_SEPARATOR = ':';
}

41
property-loader/src/main/java/com/github/weisj/darklaf/parser/DimensionParser.java

@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.List;
import javax.swing.plaf.DimensionUIResource;
public class DimensionParser extends KeyFilteredParser implements Delimiters {
public DimensionParser() {
super("Size", ".size");
}
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
List<Integer> dimensions = ParserUtil.parseDelimited(ARG_SEPARATOR, Integer.class, parseResult, context);
if (dimensions.size() != 2) return ParserUtil.error(parseResult, "Invalid dimension.");
return ParserUtil.setNonNull(parseResult,
new DimensionUIResource(dimensions.get(0), dimensions.get(1)));
}
}

41
property-loader/src/main/java/com/github/weisj/darklaf/parser/FallbackParser.java

@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
public class FallbackParser implements PropertyParser {
private static final String FALLBACK_PREFIX = "?:";
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
boolean isFallback = ParserUtil.stripPrefixFromValue(parseResult, FALLBACK_PREFIX);
if (isFallback) {
ParserUtil.replaceIfNull(parseResult, parseResult.key, context.accumulator);
ParserUtil.replaceIfNull(parseResult, parseResult.key, context.defaults);
if (!parseResult.finished) {
// Not found. Calculate fallback value.
return Parser.parse(parseResult, context);
}
}
return parseResult;
}
}

101
property-loader/src/main/java/com/github/weisj/darklaf/parser/FontParser.java

@ -0,0 +1,101 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.awt.Font;
import java.text.AttributedCharacterIterator;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.github.weisj.darklaf.uiresource.DarkFontUIResource;
import com.github.weisj.darklaf.util.FontUtil;
public class FontParser extends KeyFilteredParser implements Delimiters {
private static final Map<AttributedCharacterIterator.Attribute, Integer> attributes = Collections.emptyMap();
private static final String BASE_FONT = "font";
private static final String FONT_FROM = "from";
private static final String FONT_SIZE = "withSize";
private static final String FONT_STYLE = "withStyle";
private static final char FONT_DELIMITER = '-';
public FontParser() {
super("font");
}
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
Font base = null;
int size = -1;
int style = -1;
while (!parseResult.value.isEmpty()) {
int length = parseResult.value.length();
if (ParserUtil.stripPrefixFromValue(parseResult, BASE_FONT)) {
if (base != null) ParserUtil.warning("Duplicate Base font declared: '" + parseResult.value + "'");
base = ParserUtil
.parseBetween(ARG_START, ARG_END, this::parseBaseFont, Font.class, parseResult, context)
.orElse(base);
} else if (ParserUtil.stripPrefixFromValue(parseResult, FONT_FROM)) {
if (base != null) ParserUtil.warning("Duplicate Base font declared: '" + parseResult.value + "'");
base = ParserUtil.parseBetween(ARG_START, ARG_END, Font.class, parseResult, context)
.orElse(base);
} else if (ParserUtil.stripPrefixFromValue(parseResult, FONT_STYLE)) {
if (style >= 0) ParserUtil.warning("Duplicate font style declared: '" + parseResult.value + "'");
style = ParserUtil.parseBetween(ARG_START, ARG_END, Integer.class, parseResult, context)
.orElse(style);
} else if (ParserUtil.stripPrefixFromValue(parseResult, FONT_SIZE)) {
if (size >= 0) ParserUtil.warning("Duplicate font size declared: '" + parseResult.value + "'");
size = ParserUtil.parseDelimited(ARG_START, ARG_END, ARG_SEPARATOR, Integer.class, parseResult, context)
.stream().reduce(Integer::sum).orElse(size);
}
if (parseResult.value.length() == length) {
// Did not make any progress. Bail
return ParserUtil.error(parseResult, "Unexpected token while parsing font");
}
}
if (base == null) base = FontUtil.createFont(null, Font.PLAIN, 12);
if (size <= 0) size = base.getSize();
if (style < 0) style = base.getStyle();
// noinspection MagicConstant
Font font = base.deriveFont(style, size);
return ParserUtil.setNonNull(parseResult, new DarkFontUIResource(font.deriveFont(attributes)));
}
private ParseResult parseBaseFont(final ParseResult parseResult, final ParserContext context) {
List<Object> parts = ParserUtil.parseDelimited(FONT_DELIMITER, Object.class,
Parser.createParseResult("", parseResult.value), context);
if (parts.size() != 3) return fontStructureError(parseResult);
if (!(parts.get(0) instanceof String)) return fontStructureError(parseResult);
if (!(parts.get(1) instanceof Integer)) return fontStructureError(parseResult);
if (!(parts.get(2) instanceof Integer)) return fontStructureError(parseResult);
return ParserUtil.setNonNull(parseResult,
FontUtil.createFont((String) parts.get(0), (Integer) parts.get(1), (Integer) parts.get((2))));
}
private ParseResult fontStructureError(final ParseResult parseResult) {
return ParserUtil.error(parseResult,
"Expected structure FontName-FontStyle-FontStyle but got '" + parseResult.value + "'");
}
}

102
property-loader/src/main/java/com/github/weisj/darklaf/parser/IconParser.java

@ -0,0 +1,102 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.awt.Dimension;
import java.util.Collections;
import java.util.List;
import javax.swing.Icon;
import com.github.weisj.darklaf.icons.DarkUIAwareIcon;
import com.github.weisj.darklaf.icons.EmptyIcon;
import com.github.weisj.darklaf.icons.StateIcon;
public class IconParser extends KeyFilteredParser implements Delimiters {
private static final char MODIFIER_START = '[';
private static final char MODIFIER_END = ']';
private static final char MODIFIER_DELIMITER = ',';
private static final String DUAL_KEY = "dual";
private static final String AWARE_KEY = "aware";
private static final String THEMED_KEY = "themed";
private static final String ICON_EMPTY = "empty";
public IconParser() {
super(".icon", "Icon", "Image");
}
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
if (parseResult.value.startsWith(String.valueOf(LIST_START))
&& parseResult.value.endsWith(String.valueOf(LIST_END))) {
return parseStateIcon(parseResult, context);
}
Dimension dim = new Dimension(16, 16);
if (parseResult.value.endsWith(String.valueOf(ARG_END))) {
List<Integer> dimensions = ParserUtil.parseDelimited(ARG_START, ARG_END, ARG_SEPARATOR, false,
PropertyParser.of(Integer::parseInt), Integer.class, parseResult, context);
if (dimensions.size() != 2) return ParserUtil.error(parseResult, "Invalid dimension.");
dim.width = dimensions.get(0);
dim.height = dimensions.get(1);
}
List<String> modifiers;
if (parseResult.value.endsWith(String.valueOf(MODIFIER_END))) {
modifiers = ParserUtil.parseDelimited(MODIFIER_START, MODIFIER_END, MODIFIER_DELIMITER, false,
PropertyParser.of(s -> s), String.class, parseResult, context);
} else {
modifiers = Collections.emptyList();
}
boolean dual = modifiers.contains(DUAL_KEY);
boolean aware = modifiers.contains(AWARE_KEY);
boolean themed = modifiers.contains(THEMED_KEY);
if (aware && themed) {
return ParserUtil.error(parseResult,
"Modifiers " + AWARE_KEY + " and " + THEMED_KEY + " are mutually exclusive.");
}
Icon icon;
if (ICON_EMPTY.equals(parseResult.value)) {
icon = EmptyIcon.create(dim.width, dim.height);
} else if (themed) {
icon = context.iconLoader.getIcon(parseResult.value, dim.width, dim.height, true);
} else if (dual || aware) {
DarkUIAwareIcon awareIcon = context.iconLoader.getUIAwareIcon(parseResult.value, dim.width, dim.height);
if (dual) {
icon = awareIcon.getDual();
} else {
icon = awareIcon;
}
} else {
icon = context.iconLoader.getIcon(parseResult.value, dim.width, dim.height);
}
return ParserUtil.setNonNull(parseResult, icon);
}
private static ParseResult parseStateIcon(final ParseResult parseResult, final ParserContext context) {
return ParserUtil.setNonNull(parseResult,
new StateIcon(ParserUtil.parseDelimited(LIST_START, LIST_END, LIST_SEPARATOR,
Icon.class, parseResult, context)));
}
}

43
property-loader/src/main/java/com/github/weisj/darklaf/parser/InsetParser.java

@ -0,0 +1,43 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.List;
import javax.swing.plaf.InsetsUIResource;
public class InsetParser extends KeyFilteredParser implements Delimiters {
public InsetParser() {
super("Insets", ".insets", ".margins");
}
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
List<Integer> insetsValues = ParserUtil.parseDelimited(ARG_SEPARATOR, Integer.class, parseResult, context);
if (insetsValues.size() != 4) {
return ParserUtil.error(parseResult, "Expected 4 arguments but got " + insetsValues.size());
}
return ParserUtil.setNonNull(parseResult, new InsetsUIResource(
insetsValues.get(0), insetsValues.get(1), insetsValues.get(2), insetsValues.get(3)));
}
}

37
property-loader/src/main/java/com/github/weisj/darklaf/parser/JoinedParser.java

@ -0,0 +1,37 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
class JoinedParser implements PropertyParser {
private final PropertyParser first;
private final PropertyParser second;
JoinedParser(final PropertyParser first, final PropertyParser second) {
this.first = first;
this.second = second;
}
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
return ParserUtil.apply(second, ParserUtil.apply(first, parseResult, context), context);
}
}

38
property-loader/src/main/java/com/github/weisj/darklaf/parser/KeyFilteredParser.java

@ -0,0 +1,38 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.Arrays;
public abstract class KeyFilteredParser implements PropertyParser {
private final String[] suffixes;
public KeyFilteredParser(final String... suffixes) {
this.suffixes = suffixes;
}
@Override
public boolean filter(final ParseResult parseResult, final ParserContext context) {
return Arrays.stream(suffixes).anyMatch(it -> parseResult.key.endsWith(it));
}
}

37
property-loader/src/main/java/com/github/weisj/darklaf/parser/LazyObjectParser.java

@ -0,0 +1,37 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import javax.swing.UIDefaults;
public class LazyObjectParser extends KeyFilteredParser {
public LazyObjectParser() {
super("Border", ".border", "Renderer");
}
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
return ParserUtil.setNonNull(parseResult,
(UIDefaults.LazyValue) def -> ParserUtil.createObject(parseResult.value));
}
}

34
property-loader/src/main/java/com/github/weisj/darklaf/parser/ListParser.java

@ -0,0 +1,34 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
public class ListParser implements PropertyParser, Delimiters {
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
if (parseResult.value.startsWith(String.valueOf(LIST_START))) {
return ParserUtil.setNonNull(parseResult,
ParserUtil.parseDelimited(LIST_START, LIST_END, LIST_SEPARATOR, Object.class, parseResult,
context));
}
return parseResult;
}
}

54
property-loader/src/main/java/com/github/weisj/darklaf/parser/MapParser.java

@ -0,0 +1,54 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.List;
import java.util.stream.Collectors;
import com.github.weisj.darklaf.util.Pair;
public class MapParser implements PropertyParser, Delimiters {
private final PropertyParser pairParser = (parsable, context) -> {
if (parsable.value.contains(String.valueOf(PAIR_SEPARATOR))) {
List<Object> components = ParserUtil.parseDelimited(PAIR_SEPARATOR, Object.class, parsable, context);
if (components.size() != 2) return ParserUtil.error(parsable, "Expected 2 components");
return ParserUtil.setNonNull(parsable, new Pair<>(components.get(0), components.get(1)));
} else {
return ParserUtil.setNonNull(parsable,
new Pair<>(Parser.parse(parsable, context).result, Parser.EMPTY_VALUE));
}
};
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
if (parseResult.value.startsWith(String.valueOf(MAP_START))
&& parseResult.value.endsWith(String.valueOf(MAP_END))) {
return ParserUtil.setNonNull(parseResult,
ParserUtil.parseDelimited(MAP_START, MAP_END, MAP_SEPARATOR, pairParser,
Pair.class, parseResult, context).stream()
.collect(Collectors.toMap(Pair::getFirst, Pair::getSecond)));
}
return parseResult;
}
}

35
property-loader/src/main/java/com/github/weisj/darklaf/parser/NullParser.java

@ -0,0 +1,35 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import com.github.weisj.darklaf.util.PropertyValue;
public class NullParser implements PropertyParser {
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
if (parseResult.value == null || PropertyValue.NULL.equals(parseResult.value)) {
parseResult.result = null;
parseResult.finished = true;
}
return parseResult;
}
}

64
property-loader/src/main/java/com/github/weisj/darklaf/parser/ParseResult.java

@ -0,0 +1,64 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.ArrayList;
import java.util.List;
public class ParseResult {
public String key;
public String value;
public Object result;
public boolean finished;
public List<String> warnings = new ArrayList<>();
private String savedKey;
private String savedValue;
public ParseResult(final String key, final String value) {
this.key = key != null ? key : "";
this.value = value != null ? value : "";
}
@Override
public String toString() {
return "ParseResult{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
", result=" + result +
", finished=" + finished +
", warnings=" + warnings +
", savedKey='" + savedKey + '\'' +
", savedValue='" + savedValue + '\'' +
'}';
}
public void save() {
savedKey = key;
savedValue = value;
}
public void restore() {
key = savedKey;
value = savedValue;
}
}

88
property-loader/src/main/java/com/github/weisj/darklaf/parser/Parser.java

@ -0,0 +1,88 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.Arrays;
import java.util.List;
public final class Parser {
public static final Object EMPTY_VALUE = new Object();
private static final List<PropertyParser> steps = Arrays.asList(
new NullParser(),
new FallbackParser(),
new ReferenceParser(),
new PrimitiveParser(),
new InsetParser(),
new LazyObjectParser(),
new ActiveObjectParser(),
new FontParser(),
new IconParser(),
new DimensionParser(),
new ListParser(),
new MapParser());
private static boolean debugMode;
public static void setDebugMode(final boolean debugMode) {
Parser.debugMode = debugMode;
}
public static boolean isDebugMode() {
return debugMode;
}
public static ParseResult parse(final ParseResult parseResult, final ParserContext context) {
ParseResult p = parseResult;
String savedValue = parseResult.value;
for (PropertyParser step : steps) {
if (p.finished) return p;
p = step.parse(p, context);
}
if (!p.finished) {
for (String warning : p.warnings) {
ParserUtil.warning(warning);
}
ParserUtil.setNonNull(p, savedValue);
}
return p;
}
public static ParseResult createParseResult(final String key, final String value) {
if (isDebugMode()) {
return new DebugParseResult(key, value);
} else {
return new ParseResult(key, value);
}
}
public static class DebugParseResult extends ParseResult {
public String referenceKey;
public DebugParseResult(final String key, final String value) {
super(key, value);
}
}
}

39
property-loader/src/main/java/com/github/weisj/darklaf/parser/ParserContext.java

@ -0,0 +1,39 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.Map;
import com.github.weisj.darklaf.icons.IconLoader;
public class ParserContext {
public final Map<Object, Object> accumulator;
public final Map<Object, Object> defaults;
public final IconLoader iconLoader;
public ParserContext(final Map<Object, Object> accumulator, final Map<Object, Object> defaults,
final IconLoader iconLoader) {
this.accumulator = accumulator;
this.defaults = defaults;
this.iconLoader = iconLoader;
}
}

230
property-loader/src/main/java/com/github/weisj/darklaf/parser/ParserUtil.java

@ -0,0 +1,230 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import com.github.weisj.darklaf.util.LogUtil;
import com.github.weisj.darklaf.util.Types;
final class ParserUtil implements Delimiters {
private static final char EMPTY_CHAR = Character.MIN_VALUE;
private static final Logger LOGGER = LogUtil.getLogger(PropertyParser.class);
static ParseResult error(final ParseResult parseResult, final String message) {
LOGGER.severe("Error while parsing " + parseResult + ". " + message);
parseResult.finished = true;
parseResult.result = null;
return parseResult;
}
static void warning(final String message) {
LOGGER.warning(message);
}
static ParseResult apply(final PropertyParser parser, final ParseResult p, final ParserContext c) {
return parser != null ? parser.parse(p, c) : p;
}
static boolean stripPrefixFromKey(final ParseResult parseResult, final String prefix) {
if (parseResult.key.startsWith(prefix)) {
parseResult.key = parseResult.key.substring(prefix.length());
return true;
}
return false;
}
static boolean stripPrefixFromValue(final ParseResult parseResult, final String prefix) {
if (parseResult.value.startsWith(prefix)) {
parseResult.value = parseResult.value.substring(prefix.length());
return true;
}
return false;
}
static void replaceIfNull(final ParseResult parseResult, final String key, final Map<Object, Object> map) {
if (parseResult.finished) return;
Object obj = map.get(key);
while (obj instanceof ParseResult) {
obj = ((ParseResult) obj).result;
}
setNonNull(parseResult, obj);
}
static ParseResult setNonNull(final ParseResult parseResult, final Object result) {
if (result != null) {
parseResult.result = result;
parseResult.finished = true;
}
return parseResult;
}
static <T> Optional<T> parseBetween(final char start, final char end, final Class<T> type,
final ParseResult parseResult,
final ParserContext context) {
return parseBetween(start, end, Parser::parse, type, parseResult, context);
}
static <T> Optional<T> parseBetween(final char start, final char end, final PropertyParser parser,
final Class<T> type, final ParseResult parseResult,
final ParserContext context) {
List<T> parsed = parseDelimited(start, end, EMPTY_CHAR, parser, type, parseResult, context);
return parsed.stream().findFirst();
}
static <T> List<T> parseDelimited(final char delimiter, final PropertyParser parser, final Class<T> type,
final ParseResult parseResult, final ParserContext context) {
return parseDelimited(EMPTY_CHAR, EMPTY_CHAR, delimiter, parser, type, parseResult, context);
}
static <T> List<T> parseDelimited(final char delimiter, final Class<T> type,
final ParseResult parseResult, final ParserContext context) {
return parseDelimited(delimiter, Parser::parse, type, parseResult, context);
}
static <T> List<T> parseDelimited(final char start, final char end, final char delimiter, final Class<T> type,
final ParseResult parseResult, final ParserContext context) {
return parseDelimited(start, end, delimiter, Parser::parse, type, parseResult, context);
}
static <T> List<T> parseDelimited(final char start, final char end, final char delimiter,
final PropertyParser parser, final Class<T> type, final ParseResult parseResult,
final ParserContext context) {
return parseDelimited(start, end, delimiter, true, parser, type, parseResult, context);
}
/*
* Parses the value in a delimited fashion. The consumed part of the value will be replaced in-place
* in the Parsable object
*/
static <T> List<T> parseDelimited(final char start, final char end, final char delimiter, final boolean forward,
final PropertyParser parser, final Class<T> type, final ParseResult parseResult,
final ParserContext context) {
if (forward) {
if (start != EMPTY_CHAR) {
if (parseResult.value.charAt(0) == start) {
parseResult.value = parseResult.value.substring(1);
} else {
LOGGER.warning("Expected '" + start + "' while parsing " + parseResult.value);
}
}
} else {
if (end != EMPTY_CHAR) {
if (parseResult.value.charAt(parseResult.value.length() - 1) == end) {
parseResult.value = parseResult.value.substring(0, parseResult.value.length() - 1);
} else {
LOGGER.warning("Expected '" + start + "' while parsing " + parseResult.value);
}
}
}
List<String> values = delimitedSplit(delimiter, forward ? end : start, parseResult, forward);
if (values.size() == 0) return Collections.emptyList();
List<T> parsed = values.stream()
.map(v -> Parser.createParseResult(parseResult.key, v))
.map(p -> parser.parse(p, context))
.map(p -> {
T casted = Types.safeCast(p.result, type);
if (casted == null) LOGGER.warning("Value " + p.result + " is not of type " + type
+ ". Encountered while parsing '" + p + "' for '" + parseResult + "'");
return casted;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (!forward) Collections.reverse(parsed);
return parsed;
}
static List<String> delimitedSplit(final char delimiter, final char closingDelimiter,
final ParseResult parseResult, final boolean forward) {
String value = parseResult.value;
int openDelimiters = 0;
List<String> values = new ArrayList<>();
boolean done = false;
int i = forward ? 0 : value.length() - 1;
int step = forward ? 1 : -1;
int end = forward ? value.length() : -1;
int lastPos = forward ? 0 : value.length();
for (; i != end; i += step) {
char c = value.charAt(i);
if (openDelimiters == 0) {
if (closingDelimiter != EMPTY_CHAR && closingDelimiter == c) {
done = true;
break;
} else if (delimiter == c) {
if (forward) {
values.add(value.substring(lastPos, i));
lastPos = i + 1;
} else {
values.add(value.substring(i + 1, lastPos));
lastPos = i;
}
}
}
if (isOpenDelimiter(c)) openDelimiters++;
if (isClosingDelimiter(c)) openDelimiters--;
}
if (!done && lastPos != end) {
if (forward) {
values.add(value.substring(lastPos));
} else {
values.add(value.substring(0, lastPos));
}
parseResult.value = "";
} else {
if (forward) {
values.add(value.substring(lastPos, i));
int endIndex = closingDelimiter != EMPTY_CHAR ? i + 1 : i;
parseResult.value = parseResult.value.substring(endIndex);
} else {
values.add(value.substring(i + 1, lastPos));
parseResult.value = parseResult.value.substring(0, i);
}
}
return values;
}
static Object createObject(final String value) {
try {
return Class.forName(value).getDeclaredConstructor().newInstance();
} catch (final Exception ignored) {
}
return null;
}
private static boolean isOpenDelimiter(final char c) {
return c == LIST_START || c == MAP_START || c == ARG_START;
}
private static boolean isClosingDelimiter(final char c) {
return c == LIST_END || c == MAP_END || c == ARG_END;
}
}

58
property-loader/src/main/java/com/github/weisj/darklaf/parser/PrimitiveParser.java

@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.awt.Color;
import com.github.weisj.darklaf.color.ColorUtil;
import com.github.weisj.darklaf.uiresource.DarkColorUIResource;
import com.github.weisj.darklaf.util.PropertyValue;
public class PrimitiveParser implements PropertyParser {
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
Color c = ColorUtil.fromHex(parseResult.value, null, true);
ParserUtil.setNonNull(parseResult, c != null ? new DarkColorUIResource(c) : null);
if (parseResult.finished) return parseResult;
ParserUtil.setNonNull(parseResult, getInteger(parseResult.value));
if (parseResult.finished) return parseResult;
ParserUtil.setNonNull(parseResult, getBoolean(parseResult.value));
return parseResult;
}
private Boolean getBoolean(final String value) {
return PropertyValue.TRUE.equalsIgnoreCase(value)
? Boolean.TRUE
: PropertyValue.FALSE.equalsIgnoreCase(value)
? Boolean.FALSE
: null;
}
private static Integer getInteger(final String value) {
try {
return Integer.parseInt(value);
} catch (final NumberFormatException ignored) {
return null;
}
}
}

58
property-loader/src/main/java/com/github/weisj/darklaf/parser/PropertyParser.java

@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import com.github.weisj.darklaf.util.Lambdas;
@FunctionalInterface
public interface PropertyParser {
ParseResult doParse(final ParseResult parseResult, final ParserContext context);
default ParseResult parse(final ParseResult parseResult, final ParserContext context) {
if (parseResult.finished) return parseResult;
if (!filter(parseResult, context)) return parseResult;
parseResult.save();
ParseResult p = doParse(parseResult, context);
parseResult.save();
return p;
}
default boolean filter(final ParseResult parseResult, final ParserContext context) {
return true;
}
default PropertyParser andThen(final PropertyParser parser) {
return new JoinedParser(this, parser);
}
static <E extends Exception> PropertyParser of(final Lambdas.CheckedFunction<String, Object, E> function) {
return (p, c) -> {
try {
return ParserUtil.setNonNull(p, function.apply(p.value));
} catch (final Exception e) {
p.result = null;
p.finished = true;
}
return p;
};
}
}

46
property-loader/src/main/java/com/github/weisj/darklaf/parser/ReferenceParser.java

@ -0,0 +1,46 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
public class ReferenceParser implements PropertyParser {
private static final String REFERENCE_PREFIX = "%";
@Override
public ParseResult doParse(final ParseResult parseResult, final ParserContext context) {
ParserUtil.stripPrefixFromKey(parseResult, REFERENCE_PREFIX);
parseResult.save();
if (ParserUtil.stripPrefixFromValue(parseResult, REFERENCE_PREFIX)) {
ParserUtil.replaceIfNull(parseResult, parseResult.value, context.accumulator);
ParserUtil.replaceIfNull(parseResult, parseResult.value, context.defaults);
if (!parseResult.finished) {
parseResult.warnings.add("Could not reference value '"
+ parseResult.value + "' while loading '" + parseResult.key + "'. "
+ "Maybe it's a forward reference.");
parseResult.restore();
} else if (parseResult instanceof Parser.DebugParseResult) {
((Parser.DebugParseResult) parseResult).referenceKey = REFERENCE_PREFIX + parseResult.value;
}
}
return parseResult;
}
}

213
property-loader/src/test/java/com/github/weisj/darklaf/parser/ParserTest.java

@ -0,0 +1,213 @@
/*
* MIT License
*
* Copyright (c) 2020 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.darklaf.parser;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.swing.UIDefaults;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.github.weisj.darklaf.color.ColorUtil;
import com.github.weisj.darklaf.icons.IconLoader;
class ParserTest {
final ParserContext context = new ParserContext(
new UIDefaults(), new UIDefaults(),
IconLoader.get(ParserTest.class));
@BeforeEach
void setup() {
context.defaults.clear();
context.accumulator.clear();
}
Object parse(final String key, final String value) {
return parse(key, value, true);
}
Object parse(final String key, final String value, final boolean shouldSucceed) {
ParseResult p = Parser.parse(new ParseResult(key, value), context);
Assertions.assertEquals(shouldSucceed, p.finished);
return p.result;
}
@Test
void testPrimitives() {
Assertions.assertEquals("Hello World", parse("key", "Hello World"));
Assertions.assertEquals(false, parse("key", "false"));
Assertions.assertEquals(false, parse("key", "FALSE"));
Assertions.assertEquals(false, parse("key", "FaLsE"));
Assertions.assertEquals(true, parse("key", "true"));
Assertions.assertEquals(true, parse("key", "TRUE"));
Assertions.assertEquals(true, parse("key", "tRuE"));
for (int i = -100; i < 100; i++) {
Assertions.assertEquals(i, parse("key", String.valueOf(i)));
}
Random r = new Random();
for (int i = 0; i < 100; i++) {
Color c = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
Assertions.assertEquals(c, parse("key", ColorUtil.toHex(c)));
}
}
@Test
void testReferences() {
Assertions.assertEquals("key", Parser.parse(new ParseResult("%key", "value"), context).key);
Object obj = new Object();
context.accumulator.put("key", obj);
Assertions.assertEquals(obj, parse("key2", "%key"));
context.accumulator.clear();
context.defaults.put("key", obj);
Assertions.assertEquals(obj, parse("key2", "%key"));
}
@Test
void testFallbacks() {
Object obj = new Object();
context.accumulator.put("key1", obj);
Assertions.assertEquals(obj, parse("key1", "?:false"));
Assertions.assertEquals(false, parse("key2", "?:false"));
Object obj2 = new Object();
context.accumulator.put("key2", obj2);
Assertions.assertEquals(obj, parse("key3", "?:%key1"));
}
@Test
void testDimension() {
Random r = new Random();
for (int i = 0; i < 100; i++) {
int w = r.nextInt();
int h = r.nextInt();
Assertions.assertEquals(new Dimension(w, h), parse("test.size", w + "," + h));
Assertions.assertEquals(new Dimension(w, h), parse("testSize", w + "," + h));
}
Dimension dim = new Dimension(12, 34);
context.accumulator.put("key.width", dim.width);
Assertions.assertEquals(dim, parse("key.size", "%key.width," + dim.height));
}
@Test
void testInsets() {
Random r = new Random();
for (int i = 0; i < 100; i++) {
Insets insets = new Insets(r.nextInt(), r.nextInt(), r.nextInt(), r.nextInt());
Assertions.assertEquals(insets, parse("test.insets",
insets.top + "," + insets.left + "," + insets.bottom + "," + insets.right));
Assertions.assertEquals(insets, parse("testInsets",
insets.top + "," + insets.left + "," + insets.bottom + "," + insets.right));
Assertions.assertEquals(insets, parse("test.margins",
insets.top + "," + insets.left + "," + insets.bottom + "," + insets.right));
}
Insets ins = new Insets(1, 2, 3, 4);
context.accumulator.put("key.left", ins.left);
context.accumulator.put("key.bottom", ins.bottom);
Assertions.assertEquals(ins, parse("key.insets", ins.top + ",%key.left,%key.bottom," + ins.right));
}
@Test
void testActiveObject() {
context.accumulator.put("key", parse("key.component", "java.lang.Object"));
Assertions.assertEquals(Object.class, context.accumulator.get("key").getClass());
Assertions.assertNotEquals(context.accumulator.get("key"), context.accumulator.get("key"));
context.accumulator.put("key2", parse("key2.component", "%key"));
Assertions.assertEquals(Object.class, context.accumulator.get("key2").getClass());
Assertions.assertNotEquals(context.accumulator.get("key"), context.accumulator.get("key2"));
if (context.accumulator instanceof UIDefaults) {
// UIDefaults evaluates the active object when resolving the reference.
// Hence active values get lost.
Assertions.assertEquals(context.accumulator.get("key2"), context.accumulator.get("key2"));
} else {
Assertions.assertNotEquals(context.accumulator.get("key2"), context.accumulator.get("key2"));
}
}
@Test
void testLazyObject() {
context.accumulator.put("key", parse("key.border", "java.lang.Object"));
Assertions.assertEquals(Object.class, context.accumulator.get("key").getClass());
Assertions.assertEquals(context.accumulator.get("key"), context.accumulator.get("key"));
context.accumulator.put("key2", parse("key2.component", "%key"));
Assertions.assertEquals(Object.class, context.accumulator.get("key2").getClass());
Assertions.assertEquals(context.accumulator.get("key"), context.accumulator.get("key2"));
Assertions.assertEquals(context.accumulator.get("key2"), context.accumulator.get("key2"));
}
@Test
void testList() {
Object obj = new Object();
context.accumulator.put("key", obj);
List<Object> list = Arrays.asList("Test", false, 15, obj);
Assertions.assertEquals(list, parse("listKey", "[Test;false;15;%key]"));
List<Object> nestedList =
Arrays.asList(1, 2, Arrays.asList(3, Arrays.asList(4, 5), 6, 7), 8, Collections.singletonList(9), 10);
Assertions.assertEquals(nestedList, parse("listKey", "[1;2;[3;[4;5];6;7];8;[9];10]"));
}
@Test
void testMap() {
Map<Object, Object> map = new HashMap<>();
map.put("key1", 1);
map.put("key2", false);
map.put(3, "value3");
map.put("key4", Arrays.asList(1, 2, 3));
Assertions.assertEquals(map, parse("mapKey", "{key1:1;key2:false;3:value3;key4:[1;2;3]}"));
}
@Test
void testDelimitedSplit() {
String value1 = "a,b,[c,d,{e,f},g],(h),i,j)Suffix";
String value2 = "Prefix(a,b,[c,d,{e,f},g],(h),i,j";
List<String> expected = Arrays.asList("a", "b", "[c,d,{e,f},g]", "(h)", "i", "j");
List<String> values1 = ParserUtil.delimitedSplit(',', ')', new ParseResult("key", value1), true);
Assertions.assertEquals(expected, values1);
Collections.reverse(expected);
List<String> values2 = ParserUtil.delimitedSplit(',', '(', new ParseResult("key", value2), false);
Assertions.assertEquals(expected, values2);
}
}

10
utils/src/main/java/com/github/weisj/darklaf/color/ColorUtil.java

@ -59,8 +59,12 @@ public final class ColorUtil {
}
public static Color fromHex(final String str, final Color defaultValue) {
return fromHex(str, defaultValue, false);
}
public static Color fromHex(final String str, final Color defaultValue, final boolean fullSizeHex) {
try {
return fromHex(str);
return fromHex(str, fullSizeHex);
} catch (final Exception var3) {
return defaultValue;
}
@ -74,12 +78,12 @@ public final class ColorUtil {
return String.format("%02X%02X%02X", r, g, b);
}
public static Color fromHex(String str) {
static Color fromHex(String str, final boolean fullSizeHex) {
if (str.startsWith("#")) {
str = str.substring(1);
}
if (str.length() == 3) {
if (!fullSizeHex && str.length() == 3) {
return new Color(17 * Integer.valueOf(String.valueOf(str.charAt(0)), 16),
17 * Integer.valueOf(String.valueOf(str.charAt(1)), 16),
17 * Integer.valueOf(String.valueOf(str.charAt(2)), 16));

Loading…
Cancel
Save