Github user ahgittin commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/999#discussion_r219655496
  
    --- 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);
    --- End diff --
    
    if we just have _one way_ then it _has_ to be the `toString()` which is 
ugly to work with -- or we have a different parser for the `toString()`.  given 
that we have `Predicates.or` and `any`, it would seem natural to have `or` as a 
similar word.  the disadvantages of having multiple ways is much reduced when 
one way is explicitly preferred, esp if we are able to convert things to a 
canonical form which we do here.  if you really don't like it i could drop 
`any` and `all`, just use `or` and `and` but don't think it's worth using the 
`toString()` or insisting on a separate parser to handle the conversion from 
the `toString()`.


---

Reply via email to