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_r219561614
  
    --- 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);
    --- End diff --
    
    These are equivalent in a UI where there is just a text box, but not equivalent in an input yaml file. I'm therefore very hesitant to turn `StringPredicates.isNonBlank` into the same meaning as `Predicates.notNull`.
    
    By the way, I'm imagining we'll move to use this for parsing YAML blueprints (otherwise what is the point of making it bi-directional?). Is that your thinking as well?
    
    If parsing strings from yaml blueprints, what else should we support? For example, should we allow `StringPredicates.startsWith("a")` and other such utilities? Very happy to keep it simpler for now, and not try to add support for things like that!


---