BV2: value extraction
Project: http://git-wip-us.apache.org/repos/asf/bval/repo Commit: http://git-wip-us.apache.org/repos/asf/bval/commit/a602e414 Tree: http://git-wip-us.apache.org/repos/asf/bval/tree/a602e414 Diff: http://git-wip-us.apache.org/repos/asf/bval/diff/a602e414 Branch: refs/heads/bv2 Commit: a602e414fbf7641395afe80dd744f68e29ef13c4 Parents: fad43eb Author: Matt Benson <[email protected]> Authored: Wed Feb 21 14:35:25 2018 -0600 Committer: Matt Benson <[email protected]> Committed: Wed Feb 21 14:44:28 2018 -0600 ---------------------------------------------------------------------- .../bval/jsr/valueextraction/FxExtractor.java | 96 ++++++++++ .../IterableElementExtractor.java | 30 +++ .../valueextraction/ListElementExtractor.java | 34 ++++ .../bval/jsr/valueextraction/MapExtractor.java | 42 +++++ .../jsr/valueextraction/OptionalExtractor.java | 65 +++++++ .../jsr/valueextraction/ValueExtractors.java | 181 +++++++++++++++++++ .../DefaultExtractors.properties | 25 +++ 7 files changed, 473 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java new file mode 100644 index 0000000..15601b2 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/FxExtractor.java @@ -0,0 +1,96 @@ +/* + * 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.bval.jsr.valueextraction; + +import java.util.Optional; +import java.util.function.BooleanSupplier; + +import javax.validation.valueextraction.ExtractedValue; +import javax.validation.valueextraction.UnwrapByDefault; +import javax.validation.valueextraction.ValueExtractor; + +import org.apache.bval.util.reflection.Reflection; + +import javafx.beans.property.ReadOnlyListProperty; +import javafx.beans.property.ReadOnlyMapProperty; +import javafx.beans.property.ReadOnlySetProperty; +import javafx.beans.value.ObservableValue; + +@SuppressWarnings("restriction") +public abstract class FxExtractor { + public static class Activation implements BooleanSupplier { + + @Override + public boolean getAsBoolean() { + try { + return Reflection.toClass("javafx.beans.Observable") != null; + } catch (ClassNotFoundException e) { + return false; + } + } + } + + @UnwrapByDefault + public static class ForObservableValue implements ValueExtractor<ObservableValue<@ExtractedValue ?>> { + + @Override + public void extractValues(ObservableValue<?> originalValue, ValueExtractor.ValueReceiver receiver) { + receiver.value(null, originalValue.getValue()); + } + } + + public static class ForListProperty implements ValueExtractor<ReadOnlyListProperty<@ExtractedValue ?>> { + + @Override + public void extractValues(ReadOnlyListProperty<?> originalValue, ValueExtractor.ValueReceiver receiver) { + Optional.ofNullable(originalValue.getValue()).ifPresent(l -> { + for (int i = 0, sz = l.size(); i < sz; i++) { + receiver.indexedValue("<list element>", i, l.get(i)); + } + }); + } + } + + public static class ForSetProperty implements ValueExtractor<ReadOnlySetProperty<@ExtractedValue ?>> { + + @Override + public void extractValues(ReadOnlySetProperty<?> originalValue, ValueExtractor.ValueReceiver receiver) { + Optional.ofNullable(originalValue.getValue()) + .ifPresent(s -> s.forEach(e -> receiver.iterableValue("<iterable element>", e))); + } + } + + public static class ForMapPropertyKey implements ValueExtractor<ReadOnlyMapProperty<@ExtractedValue ?, ?>> { + + @Override + public void extractValues(ReadOnlyMapProperty<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) { + Optional.ofNullable(originalValue.getValue()) + .ifPresent(m -> m.keySet().forEach(k -> receiver.keyedValue("<map key>", k, k))); + } + } + + public static class ForMapPropertyValue implements ValueExtractor<ReadOnlyMapProperty<?, @ExtractedValue ?>> { + + @Override + public void extractValues(ReadOnlyMapProperty<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) { + Optional.ofNullable(originalValue.getValue()).ifPresent( + m -> m.entrySet().forEach(e -> receiver.keyedValue("<map value>", e.getKey(), e.getValue()))); + } + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java new file mode 100644 index 0000000..8fd2fc0 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/IterableElementExtractor.java @@ -0,0 +1,30 @@ +/* + * 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.bval.jsr.valueextraction; + +import javax.validation.valueextraction.ExtractedValue; +import javax.validation.valueextraction.ValueExtractor; + +public class IterableElementExtractor implements ValueExtractor<Iterable<@ExtractedValue ?>> { + + @Override + public void extractValues(Iterable<?> originalValue, ValueExtractor.ValueReceiver receiver) { + originalValue.forEach(v -> receiver.iterableValue("<iterable element>", v)); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java new file mode 100644 index 0000000..bd84726 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ListElementExtractor.java @@ -0,0 +1,34 @@ +/* + * 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.bval.jsr.valueextraction; + +import java.util.List; + +import javax.validation.valueextraction.ExtractedValue; +import javax.validation.valueextraction.ValueExtractor; + +public class ListElementExtractor implements ValueExtractor<List<@ExtractedValue ?>> { + + @Override + public void extractValues(List<?> originalValue, ValueExtractor.ValueReceiver receiver) { + for (int i = 0, sz = originalValue.size(); i < sz; i++) { + receiver.indexedValue("<list element>", i, originalValue.get(i)); + } + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java new file mode 100644 index 0000000..a6848b8 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/MapExtractor.java @@ -0,0 +1,42 @@ +/* + * 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.bval.jsr.valueextraction; + +import java.util.Map; + +import javax.validation.valueextraction.ExtractedValue; +import javax.validation.valueextraction.ValueExtractor; + +public abstract class MapExtractor { + public static class ForKey implements ValueExtractor<Map<@ExtractedValue ?, ?>> { + + @Override + public void extractValues(Map<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) { + originalValue.keySet().forEach(k -> receiver.keyedValue("<map key>", k, k)); + } + } + + public static class ForValue implements ValueExtractor<Map<?, @ExtractedValue ?>> { + + @Override + public void extractValues(Map<?, ?> originalValue, ValueExtractor.ValueReceiver receiver) { + originalValue.entrySet().forEach(e -> receiver.keyedValue("<map value>", e.getKey(), e.getValue())); + } + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java new file mode 100644 index 0000000..5f073cc --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/OptionalExtractor.java @@ -0,0 +1,65 @@ +/* + * 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.bval.jsr.valueextraction; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import javax.validation.valueextraction.ExtractedValue; +import javax.validation.valueextraction.UnwrapByDefault; +import javax.validation.valueextraction.ValueExtractor; + +public abstract class OptionalExtractor { + public static class ForObject implements ValueExtractor<Optional<@ExtractedValue ?>> { + + @Override + public void extractValues(Optional<?> originalValue, ValueExtractor.ValueReceiver receiver) { + receiver.value(null, originalValue.orElse(null)); + } + } + + @UnwrapByDefault + public static class ForInt implements ValueExtractor<@ExtractedValue(type = Integer.class) OptionalInt> { + + @Override + public void extractValues(OptionalInt originalValue, ValueExtractor.ValueReceiver receiver) { + receiver.value(null, originalValue.isPresent() ? Integer.valueOf(originalValue.getAsInt()) : null); + } + } + + @UnwrapByDefault + public static class ForLong implements ValueExtractor<@ExtractedValue(type = Long.class) OptionalLong> { + + @Override + public void extractValues(OptionalLong originalValue, ValueExtractor.ValueReceiver receiver) { + receiver.value(null, originalValue.isPresent() ? Long.valueOf(originalValue.getAsLong()) : null); + } + } + + @UnwrapByDefault + public static class ForDouble implements ValueExtractor<@ExtractedValue(type = Double.class) OptionalDouble> { + + @Override + public void extractValues(OptionalDouble originalValue, ValueExtractor.ValueReceiver receiver) { + receiver.value(null, originalValue.isPresent() ? Double.valueOf(originalValue.getAsDouble()) : null); + } + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java new file mode 100644 index 0000000..50feb6c --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/valueextraction/ValueExtractors.java @@ -0,0 +1,181 @@ +/* + * 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.bval.jsr.valueextraction; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.valueextraction.ValueExtractor; +import javax.validation.valueextraction.ValueExtractorDeclarationException; +import javax.validation.valueextraction.ValueExtractorDefinitionException; + +import org.apache.bval.jsr.metadata.ContainerElementKey; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; +import org.apache.bval.util.StringUtils; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.Reflection; +import org.apache.bval.util.reflection.TypeUtils; + +public class ValueExtractors { + public static final ValueExtractors DEFAULT; + + static { + DEFAULT = new ValueExtractors(null) { + { + final Properties defaultExtractors = new Properties(); + try { + defaultExtractors.load(ValueExtractors.class.getResourceAsStream("DefaultExtractors.properties")); + } catch (IOException e) { + throw new IllegalStateException(e); + } + split(defaultExtractors.getProperty(ValueExtractor.class.getName())).map(cn -> { + try { + @SuppressWarnings("unchecked") + final Class<? extends ValueExtractor<?>> result = + (Class<? extends ValueExtractor<?>>) Reflection.toClass(cn) + .asSubclass(ValueExtractor.class); + return result; + } catch (Exception e) { + throw new IllegalStateException(e); + } + }).map(ValueExtractors::newInstance).forEach(super::add); + + split(defaultExtractors.getProperty(ValueExtractor.class.getName() + ".container")) + .flatMap(ValueExtractors::loadValueExtractors).forEach(super::add); + } + + @Override + public void add(ValueExtractor<?> extractor) { + throw new UnsupportedOperationException(); + } + }; + } + + public static Class<?> getExtractedType(ValueExtractor<?> extractor, Type target) { + final ContainerElementKey key = ContainerElementKey.forValueExtractor(extractor); + Type result = key.getAnnotatedType().getType(); + if (result instanceof TypeVariable<?>) { + result = TypeUtils.getTypeArguments(target, key.getContainerClass()).get(result); + } + Exceptions.raiseUnless(result instanceof Class<?>, ValueExtractorDefinitionException::new, + "%s did not resolve to a %s relative to %s", key, Class.class.getName(), target); + return (Class<?>) result; + } + + private static Stream<String> split(String s) { + return Stream.of(StringUtils.split(s, ',')); + } + + private static <T> T newInstance(Class<T> t) { + try { + return t.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + private static Stream<ValueExtractor<?>> loadValueExtractors(String containerClassName) { + try { + final Class<? extends BooleanSupplier> activation = + Reflection.toClass(containerClassName + "$Activation").asSubclass(BooleanSupplier.class); + if (!newInstance(activation).getAsBoolean()) { + return Stream.empty(); + } + } catch (ClassNotFoundException e) { + // always active + } + final Class<?> containerClass; + try { + containerClass = Reflection.toClass(containerClassName); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } + return Stream.of(containerClass.getClasses()).filter(ValueExtractor.class::isAssignableFrom).map(c -> { + @SuppressWarnings("unchecked") + final Class<? extends ValueExtractor<?>> result = + (Class<? extends ValueExtractor<?>>) c.asSubclass(ValueExtractor.class); + return result; + }).map(ValueExtractors::newInstance); + } + + private final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> valueExtractors = new Lazy<>(HashMap::new); + private final ValueExtractors parent; + + public ValueExtractors() { + this(DEFAULT); + } + + private ValueExtractors(ValueExtractors parent) { + this.parent = parent; + } + + public ValueExtractors createChild() { + return new ValueExtractors(this); + } + + public void add(ValueExtractor<?> extractor) { + Validate.notNull(extractor); + valueExtractors.get().compute(ContainerElementKey.forValueExtractor(extractor), (k, v) -> { + Exceptions.raiseIf(v != null, ValueExtractorDeclarationException::new, + "Multiple context-level %ss specified for %s", ValueExtractor.class.getSimpleName(), k); + return extractor; + }); + } + + public Map<ContainerElementKey, ValueExtractor<?>> getValueExtractors() { + final Lazy<Map<ContainerElementKey, ValueExtractor<?>>> result = new Lazy<>(HashMap::new); + populate(result); + return result.optional().orElseGet(Collections::emptyMap); + } + + public ValueExtractor<?> find(ContainerElementKey key) { + final Map<ContainerElementKey, ValueExtractor<?>> allValueExtractors = getValueExtractors(); + if (allValueExtractors.containsKey(key)) { + return allValueExtractors.get(key); + } + // search for assignable ContainerElementKey: + Set<ContainerElementKey> assignableKeys = key.getAssignableKeys(); + while (!assignableKeys.isEmpty()) { + final Optional<ValueExtractor<?>> found = assignableKeys.stream().filter(allValueExtractors::containsKey) + .<ValueExtractor<?>> map(allValueExtractors::get).findFirst(); + if (found.isPresent()) { + return found.get(); + } + assignableKeys = assignableKeys.stream().map(ContainerElementKey::getAssignableKeys) + .flatMap(Collection::stream).collect(Collectors.toSet()); + } + return null; + } + + protected void populate(Supplier<Map<ContainerElementKey, ValueExtractor<?>>> target) { + Optional.ofNullable(parent).ifPresent(p -> p.populate(target)); + valueExtractors.optional().ifPresent(m -> target.get().putAll(m)); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/a602e414/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties b/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties new file mode 100644 index 0000000..9d9b768 --- /dev/null +++ b/bval-jsr/src/main/resources/org/apache/bval/jsr/valueextraction/DefaultExtractors.properties @@ -0,0 +1,25 @@ +# 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. + +javax.validation.valueextraction.ValueExtractor=\ + org.apache.bval.jsr.valueextraction.IterableElementExtractor,\ + org.apache.bval.jsr.valueextraction.ListElementExtractor + +javax.validation.valueextraction.ValueExtractor.container=\ + org.apache.bval.jsr.valueextraction.MapExtractor,\ + org.apache.bval.jsr.valueextraction.OptionalExtractor,\ + org.apache.bval.jsr.valueextraction.FxExtractor
