osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GitHub] brooklyn-server pull request #999: Constraints - serialization and {forbidde...


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

    https://github.com/apache/brooklyn-server/pull/999#discussion_r219558955
  
    --- 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 --
    
    I prefer us supporting just one name.
    
    The advantage of one way is that our docs and examples are all consistent, it's simpler, and folk won't get distracted by the difference when troubleshooting (e.g. experience brooklyn devs still say "try doing X instead of Y" when both X and Y are supported, because that dev is used to doing X so knows that way works but isn't as certain about Y).
    
    The advantage of both ways is that folk can guess easier, and you can use the name you prefer for demos.
    
    I therefore think *one way* is far better, on balance.


---