Github user aledsage commented on a diff in the pull request:
https://github.com/apache/brooklyn-server/pull/999#discussion_r219559231
--- Diff:
core/src/main/java/org/apache/brooklyn/core/objs/ConstraintSerialization.java
---
@@ -0,0 +1,369 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.brooklyn.core.objs;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigConstraints;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.core.ResourcePredicates;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
+import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class ConstraintSerialization {
+
+ private final Map<String, String> predicateToStringToPreferredName =
MutableMap.of();
+ private final Map<String, Function<List<?>,Predicate<?>>>
predicatePreferredNameToConstructor = MutableMap.of();
+
+ public static class PredicateSerializationRuleAdder<T> {
+ private String preferredName;
+ private Function<List<?>, T> constructorArgsFromList;
+ private Function<T, Predicate<?>> constructor;
+ private Predicate<?> predicateSample;
+ private T constructorSampleInput;
+ private Set<String> equivalentNames = MutableSet.of();
+ private Set<Predicate<?>> equivalentPredicateSamples =
MutableSet.of();
+
+ ConstraintSerialization serialization;
+
+ public PredicateSerializationRuleAdder(Function<T, Predicate<?>>
constructor, Function<List<?>, T> constructorArgsFromList, T
constructorSampleInput) {
+ this.constructorArgsFromList = constructorArgsFromList;
+ this.constructor = constructor;
+ this.constructorSampleInput = constructorSampleInput;
+ }
+
+ public static PredicateSerializationRuleAdder<List<Predicate<?>>>
predicateListConstructor(Function<List<Predicate<?>>,Predicate<?>> constructor)
{
+ PredicateSerializationRuleAdder<List<Predicate<?>>> result =
new PredicateSerializationRuleAdder<List<Predicate<?>>>(constructor,
+ null, MutableList.of());
+ result.constructorArgsFromList = o ->
result.serialization.toPredicateListFromJsonList(o);
+ return result;
+ }
+
+ public static PredicateSerializationRuleAdder<String>
stringConstructor(Function<String,Predicate<?>> constructor) {
+ return new
PredicateSerializationRuleAdder<String>(constructor,
+ o -> Strings.toString(Iterables.getOnlyElement(o)), "");
+ }
+
+ public static PredicateSerializationRuleAdder<Void>
noArgConstructor(Supplier<Predicate<?>> constructor) {
+ return new PredicateSerializationRuleAdder<Void>(
+ (o) -> constructor.get(), o -> null, null);
+ }
+
+ /** Preferred name for predicate when serializing. Defaults to the
predicate name in the output of the {@link #sample(Predicate)}. */
+ public PredicateSerializationRuleAdder<T> preferredName(String
preferredName) {
+ this.preferredName = preferredName;
+ return this;
+ }
+
+ /** Other predicates which are different to the type indicated by
{@link #sample(Predicate)} but equivalent,
+ * and after serialization will be represented by {@link
#preferredName} and after deserialization
+ * will result in the {@link Predicate} produced by {@link
#constructor}. */
+ public PredicateSerializationRuleAdder<T> equivalentNames(String
...equivs) {
+ for (String equiv: equivs) equivalentNames.add(equiv);
+ return this;
+ }
+
+ /** Sample of what the {@link #constructor} will produce, used to
recognise this rule when parsing.
+ * Can be omitted if {@link #sampleArg(Object)} supplied or its
default is accepted. */
+ public PredicateSerializationRuleAdder<T> sample(Predicate<?>
samplePreferredPredicate) {
+ predicateSample = samplePreferredPredicate;
+ return this;
+ }
+
+ /** This should supply args accepted by {@link #constructor} to
generate a {@link #sample(Predicate)}.
+ * At most one of this or {@link #sample(Predicate)} should be
supplied.
+ * If the constructor accepts a default empty list/string/null
then these can be omitted. */
+ public PredicateSerializationRuleAdder<T> sampleArg(T arg) {
+ constructorSampleInput = arg;
+ return this;
+ }
+
+ /** Other predicates which are different to the type indicated by
{@link #sample(Predicate)} but equivalent,
+ * and after serialization will be represented by {@link
#preferredName} and after deserialization
+ * will result in the {@link Predicate} produced by {@link
#constructor}. */
+ public PredicateSerializationRuleAdder<T>
equivalentPredicates(Predicate<?> ...equivs) {
+ for (Predicate<?> equiv: equivs)
equivalentPredicateSamples.add(equiv);
+ return this;
+ }
+
+ public void add(ConstraintSerialization constraintSerialization) {
+ this.serialization = constraintSerialization;
+ if (predicateSample==null) predicateSample =
constructor.apply(constructorSampleInput);
+ String toStringName =
Strings.removeAfter(Preconditions.checkNotNull(predicateSample, "sample or
sampleArg must be supplied").toString(), "(", false);
+ if (preferredName==null) {
+ preferredName = toStringName;
+ } else {
+
constraintSerialization.predicateToStringToPreferredName.put(preferredName,
preferredName);
+ }
+
constraintSerialization.predicateToStringToPreferredName.put(toStringName,
preferredName);
+
+ for (String equiv: equivalentNames) {
+
constraintSerialization.predicateToStringToPreferredName.put(equiv,
preferredName);
+ }
+
+
constraintSerialization.predicatePreferredNameToConstructor.put(preferredName,
constructor.compose(constructorArgsFromList));
+
+ for (Predicate<?> equiv: equivalentPredicateSamples) {
+ String equivToStringName =
Strings.removeAfter(equiv.toString(), "(", false);
+
constraintSerialization.predicateToStringToPreferredName.put(equivToStringName,
preferredName);
+ }
+ }
+ }
+
+ private static String GROUP(String in) { return "("+in+")"; }
+ private static String NOT_CHARSET(String ...in) { return
"[^"+Strings.join(in, "")+"]"; }
+ private static String OR_GROUP(String ...in) { return
GROUP(Strings.join(in, "|")); }
+ private static String ZERO_OR_MORE(String in) { return in + "*"; }
+
+ private static String DOUBLE_QUOTED_STRING =
"\""+GROUP(ZERO_OR_MORE(OR_GROUP(NOT_CHARSET("\\", "\""), "\\.")))+"\"";
+ private static String SINGLE_QUOTED_STRING =
"\'"+GROUP(ZERO_OR_MORE(OR_GROUP(NOT_CHARSET("\\", "\'"), "\\.")))+"\'";
+
+ private static String PREDICATE = "[A-Za-z0-9_\\-\\.]+";
+
+ private static Pattern PATTERN_START_WITH_QUOTED_STRING =
Pattern.compile("^"+OR_GROUP(DOUBLE_QUOTED_STRING, SINGLE_QUOTED_STRING));
+ private static Pattern PATTERN_START_WITH_PREDICATE =
Pattern.compile("^"+GROUP(PREDICATE));
+
+ {
+ init();
+ }
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private void init() {
+ PredicateSerializationRuleAdder.predicateListConstructor((o) ->
ConfigConstraints.required()).
+ equivalentPredicates(Predicates.notNull(),
StringPredicates.isNonBlank()).add(this);
+
+ PredicateSerializationRuleAdder.predicateListConstructor((o) ->
Predicates.or((Iterable)o)).preferredName("any").equivalentNames("or").add(this);
+ PredicateSerializationRuleAdder.predicateListConstructor((o) -> /*
and predicate is default when given list */
toPredicateFromJson(o)).preferredName("all").sample(Predicates.and(Collections.emptyList())).equivalentNames("and").add(this);
+
PredicateSerializationRuleAdder.noArgConstructor(Predicates::alwaysFalse).add(this);
+
PredicateSerializationRuleAdder.noArgConstructor(Predicates::alwaysTrue).add(this);
+
+
PredicateSerializationRuleAdder.noArgConstructor(ResourcePredicates::urlExists).preferredName("urlExists").add(this);
+
PredicateSerializationRuleAdder.noArgConstructor(StringPredicates::isBlank).add(this);
+
+
PredicateSerializationRuleAdder.stringConstructor(StringPredicates::matchesRegex).preferredName("regex").add(this);
+
PredicateSerializationRuleAdder.stringConstructor(StringPredicates::matchesGlob).preferredName("glob").add(this);
+
+
PredicateSerializationRuleAdder.stringConstructor(ConfigConstraints::forbiddenIf).add(this);
+
PredicateSerializationRuleAdder.stringConstructor(ConfigConstraints::forbiddenUnless).add(this);
+
PredicateSerializationRuleAdder.stringConstructor(ConfigConstraints::requiredIf).add(this);
+
PredicateSerializationRuleAdder.stringConstructor(ConfigConstraints::requiredUnless).add(this);
+ }
+
+ public static ConstraintSerialization INSTANCE = new
ConstraintSerialization();
--- End diff --
Make it final, to avoid really weird behaviour things if someone
accidentally changes it!
---