AMBARI-16437. Add conditional constraints for Kerberos identities to control when they are created (rlevas)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/198f5880 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/198f5880 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/198f5880 Branch: refs/heads/branch-2.4 Commit: 198f588080b002956d5235343a1aeaa9f6ad3a94 Parents: 384a4be Author: Robert Levas <[email protected]> Authored: Sat May 28 08:02:00 2016 -0400 Committer: Jonathan Hurley <[email protected]> Committed: Sat May 28 14:59:04 2016 -0400 ---------------------------------------------------------------------- .../ambari/server/collections/Predicate.java | 82 +++++++++++ .../server/collections/PredicateUtils.java | 111 ++++++++++++++ .../collections/functors/AndPredicate.java | 96 +++++++++++++ .../collections/functors/ContainsPredicate.java | 138 ++++++++++++++++++ .../functors/ContextTransformer.java | 142 ++++++++++++++++++ .../DelegatedMultiplePredicateContainer.java | 114 +++++++++++++++ .../DelegatedSinglePredicateContainer.java | 110 ++++++++++++++ .../collections/functors/EqualsPredicate.java | 115 +++++++++++++++ .../collections/functors/NotPredicate.java | 80 +++++++++++ .../functors/OperationPredicate.java | 112 +++++++++++++++ .../collections/functors/OrPredicate.java | 96 +++++++++++++ .../functors/PredicateClassFactory.java | 58 ++++++++ .../server/collections/PredicateUtilsTest.java | 143 +++++++++++++++++++ .../collections/functors/AndPredicateTest.java | 108 ++++++++++++++ .../functors/ContainsPredicateTest.java | 91 ++++++++++++ .../functors/ContextTransformerTest.java | 75 ++++++++++ .../functors/EqualsPredicateTest.java | 89 ++++++++++++ .../collections/functors/NotPredicateTest.java | 89 ++++++++++++ .../collections/functors/OrPredicateTest.java | 107 ++++++++++++++ 19 files changed, 1956 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java new file mode 100644 index 0000000..09bc7d5 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/Predicate.java @@ -0,0 +1,82 @@ +/* + * 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.ambari.server.collections; + +import com.google.gson.Gson; + +import java.util.Map; + +/** + * {@link Predicate} wraps {@link org.apache.commons.collections.Predicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + */ +public abstract class Predicate implements org.apache.commons.collections.Predicate { + + /** + * The name of this predicate. For example "and", "or", etc... + */ + private final String name; + + protected Predicate(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + /** + * Serialize this {@link Predicate} to a {@link Map} + * + * @return a {@link Map} + */ + public abstract Map<String, Object> toMap(); + + /** + * Serialize this {@link Predicate} to a JSON-formatted {@link String} + * + * @return a JSON-formatted {@link String} + */ + public String toJSON() { + Map<String, Object> map = toMap(); + return (map == null) ? null : new Gson().toJson(map); + } + + + @Override + public int hashCode() { + return 37 * ((name == null) ? 0 : name.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if ((obj instanceof Predicate) && (hashCode() == obj.hashCode())) { + Predicate p = (Predicate) obj; + return (name == null) ? (p.name == null) : name.equals(p.name); + } else { + return false; + } + } + +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java new file mode 100644 index 0000000..2a75d81 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/PredicateUtils.java @@ -0,0 +1,111 @@ +/* + * 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.ambari.server.collections; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; +import org.apache.ambari.server.collections.functors.PredicateClassFactory; +import org.apache.commons.lang.StringUtils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.Map; + +/** + * PredicateUtils is a utility class providing methods to help perform tasks on {@link Predicate}s. + */ +public class PredicateUtils { + private static final Type PARSED_TYPE = new TypeToken<Map<String, Object>>() { + }.getType(); + + /** + * Safely serializes the specified {@link Predicate} to a {@link Map} + * + * @param predicate the {@link Predicate} to process + * @return a {@link Map} or null of the supplied predicate is null + */ + public static Map<String, Object> toMap(Predicate predicate) { + return (predicate == null) ? null : predicate.toMap(); + } + + /** + * Builds a {@link Predicate} from a {@link Map} + * + * @param map a map containing the details of the predicate to create. + * @return a {@link Predicate} + */ + public static Predicate fromMap(Map<String, Object> map) { + Predicate predicate = null; + + if ((map != null) && !map.isEmpty()) { + if (map.size() == 1) { + Map.Entry<String, Object> entry = map.entrySet().iterator().next(); + String name = entry.getKey(); + + Class<? extends Predicate> predicateClass = PredicateClassFactory.getPredicateClass(name); + + if (predicateClass == null) { + throw new IllegalArgumentException(String.format("Unexpected predicate name - %s", name)); + } else { + try { + // Dynamically locate and invoke the static toMap method for the named Predicate + // implementation using reflection + Method method = predicateClass.getMethod("fromMap", Map.class); + if (method == null) { + throw new UnsupportedOperationException(String.format("Cannot translate data to a %s - %s", predicateClass.getName(), "Failed to find toMap method")); + } else { + predicate = (Predicate) method.invoke(null, Collections.singletonMap(name, entry.getValue())); + } + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new UnsupportedOperationException(String.format("Cannot translate data to a %s - %s", predicateClass.getName(), e.getLocalizedMessage()), e); + } + } + } else { + throw new IllegalArgumentException(String.format("Too many map entries have been encountered - %d", map.size())); + } + } + + return predicate; + } + + + /** + * Safely serializes the specified {@link Predicate} to a JSON-formatted {@link String} + * + * @param predicate the {@link Predicate} to process + * @return a {@link String} or null of the supplied predicate is null + */ + public static String toJSON(Predicate predicate) { + return (predicate == null) ? null : predicate.toJSON(); + } + + /** + * Builds a {@link Predicate} from a JSON-formatted {@link String} + * + * @param json a string containing the details of the predicate to create. + * @return a {@link Predicate} + * @see #fromMap(Map) + */ + public static Predicate fromJSON(String json) { + Map<String, Object> map = new Gson().fromJson(json, PARSED_TYPE); + return (StringUtils.isEmpty(json) ? null : fromMap(map)); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.java new file mode 100644 index 0000000..62a953a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/AndPredicate.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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.PredicateUtils; +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * {@link AndPredicate} wraps {@link org.apache.commons.collections.functors.AndPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + * <p> + * See {@link DelegatedMultiplePredicateContainer} + */ +public class AndPredicate extends DelegatedMultiplePredicateContainer { + /** + * The name of this {@link org.apache.ambari.server.collections.Predicate} implementation + */ + public static final String NAME = "and"; + + /** + * Creates a new {@link AndPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "and" and the value is a {@link Collection} of {@link Map}s representing the contained + * predicates. + * + * @return a new {@link AndPredicate} + */ + public static AndPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof Map) && (d2 instanceof Map)) { + return new AndPredicate(PredicateUtils.fromMap((Map) d1), PredicateUtils.fromMap((Map) d2)); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types for predicates: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor. + * + * @param predicate1 the first predicate to process + * @param predicate2 the second predicate to process (if the first one yields <code>true</code> + */ + public AndPredicate(Predicate predicate1, Predicate predicate2) { + super(NAME, + (PredicateDecorator) org.apache.commons.collections.functors.AndPredicate.getInstance(predicate1, predicate2)); + } + + @Override + public int hashCode() { + return super.hashCode() + this.getClass().getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || + ((obj != null) && (super.equals(obj) && this.getClass().isInstance(obj) && (hashCode() == obj.hashCode()))); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java new file mode 100644 index 0000000..503405e --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContainsPredicate.java @@ -0,0 +1,138 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * {@link ContainsPredicate} is a predicate implementation testing whether a string value exists + * within some set of strings. + * <p> + * It it expected that the data supplied to the {@link #evaluate(Object)} method is as {@link Set} + * of {@link String}s retrieved from a context (a {@link Map} of keys to values, where each value + * can be an explicit value or a {@link Map} of values creating a tree structure. + */ +public class ContainsPredicate extends OperationPredicate { + + /** + * The name of this {@link Predicate} implementation + */ + public static final String NAME = "contains"; + + /** + * The value to test for + */ + private final String value; + + /** + * Creates a new {@link ContainsPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "contains" and the value is a {@link Collection} of 2 {@link String}s. The first value + * should be the key to use when retrieving data from the context, which should resolve to a + * {@link Set} of {@link String}s (or null). The second value should be the value to check for + * existing in the relevant set. + * + * @return a new {@link ContainsPredicate} + */ + public static ContainsPredicate fromMap(Map<String, Object> map) { + + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof String) && (d2 instanceof String)) { + return new ContainsPredicate(new ContextTransformer((String) d1), (String) d2); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor + * + * @param transformer the {@link ContextTransformer} configured with the context key to find + * @param value the value to test + */ + public ContainsPredicate(ContextTransformer transformer, String value) { + super(NAME, transformer); + this.value = value; + } + + /** + * Gets the test value + * + * @return a string + */ + public String getValue() { + return value; + } + + @Override + protected boolean evaluateTransformedData(Object data) { + return (this.value != null) && (data instanceof Set) && ((Set<?>) data).contains(this.value); + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(NAME, + new ArrayList<String>(Arrays.asList(getContextKey(), value))); + } + + @Override + public int hashCode() { + return super.hashCode() + + (37 * ((this.value == null) ? 0 : this.value.hashCode())); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof ContainsPredicate) && (hashCode() == obj.hashCode())) { + ContainsPredicate p = (ContainsPredicate) obj; + return (value == null) ? (p.value == null) : value.equals(p.value); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java new file mode 100644 index 0000000..b20963a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/ContextTransformer.java @@ -0,0 +1,142 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.commons.collections.Transformer; + +import java.util.Map; + +/** + * {@link ContextTransformer} is a {@link Transformer} implementation that traverses a {@link Map} + * of {@link Map}s to find the value related to the specified key. + * <p> + * This implementation first checks the context map for the explicit key. If the key exists, it + * returns the value associated with it. If the key does not exist and appears to represent a path + * via "/"-delimited keys (configurations/service-site/property_name), the path is traversed + * recursively looking for the requested data. + * <p> + * Example context: + * <pre> + * |- services : [set of services] + * | + * |- configurations + * | |- service-site + * | | |- property : [value] + * | + * |- key/looks/like/path : [value2] + * </pre> + * <ul> + * <li>If the key was <code>services</code>, <code>[set of services]</code> would be returned</li> + * <li>If the key was <code>configurations/service-site/property</code>, <code>value</code> would be returned</li> + * <li>If the key was <code>configurations/service-site</code>, <code>[map of service-site properties]</code> would be returned</li> + * <li>If the key was <code>key/looks/like/path</code>, <code>value2</code> would be returned</li> + * </ul> + */ +public class ContextTransformer implements Transformer { + + /** + * The key to search for + */ + private final String key; + + /** + * Constructor. + * + * @param key the key to search for + */ + public ContextTransformer(String key) { + this.key = key; + } + + /** + * Returns the key this transformer is using to search for data + * + * @return a string + */ + public String getKey() { + return key; + } + + @Override + public Object transform(Object o) { + return transform(key, o); + } + + /** + * Traverses the input object (expected to be a {@link Map}) to find the data associated with + * the specified key. + * <p> + * Note: This method is recursive in the even the key represents a path + * + * @param key the key to search for + * @param o the object containing data to process + * @return the found data or null + */ + private Object transform(String key, Object o) { + Object transformedData = null; + + if (key != null) { + if (o instanceof Map) { + Map<?, ?> data = (Map) o; + + if (data.containsKey(key)) { + transformedData = data.get(key); + } else { + // See if the key implies a tree that needs to be traversed... + // For example: configurations/service-conf/property_name + // A map of maps is expected such that the top level map has a map identified by the key + // of "configuration". The "configuration" map has a map identified by the key of + // "service-conf". The "service-conf" has a value identified by the key of "property_name". + String[] parts = key.split("\\/", 2); + + // If only a single item is returned, than the key does not indicate a tree. + if (parts.length == 2) { + // If the first item is empty, a leading "/" was encountered... retry with the pruned key + if (parts[0].isEmpty()) { + transformedData = transform(parts[1], o); + } else { + transformedData = transform(parts[1], data.get(parts[0])); + } + } + } + } + } + + return transformedData; + } + + @Override + public int hashCode() { + return (37 * ((key == null) ? 0 : key.hashCode())); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if ((obj instanceof ContextTransformer) && (hashCode() == obj.hashCode())) { + ContextTransformer t = (ContextTransformer) obj; + return (key == null) ? (t.key == null) : key.equals(t.key); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java new file mode 100644 index 0000000..b9add7a --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedMultiplePredicateContainer.java @@ -0,0 +1,114 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * DelegatedMultiplePredicateContainer is an abstract class providing functionality related to + * managing a delegate used to hold multiple {@link Predicate}s. For example <code>and</code> and + * <code>or</code> operators. + */ +abstract class DelegatedMultiplePredicateContainer extends Predicate implements PredicateDecorator { + + /** + * The delegate {@link PredicateDecorator} used to handle the internal logic for the container + */ + private final PredicateDecorator delegate; + + /** + * Constructor. + * + * @param name the name of this predicate + * @param delegate the delegate used to handle the internal logic for the container + */ + DelegatedMultiplePredicateContainer(String name, PredicateDecorator delegate) { + super(name); + this.delegate = delegate; + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(getName(), containedPredicatesToMaps()); + } + + @Override + public boolean evaluate(Object o) { + return delegate.evaluate(o); + } + + @Override + public org.apache.commons.collections.Predicate[] getPredicates() { + return delegate.getPredicates(); + } + + @Override + public int hashCode() { + return super.hashCode() + ((delegate == null) ? 0 : delegate.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof DelegatedMultiplePredicateContainer) && (hashCode() == obj.hashCode())) { + DelegatedMultiplePredicateContainer p = (DelegatedMultiplePredicateContainer) obj; + return (delegate == null) ? (p.delegate == null) : delegate.equals(p.delegate); + } else { + return false; + } + } + + /** + * Processes the contained predicates into a {@link List} of {@link Map}s. + * <p> + * This is used to serialize this {@link DelegatedMultiplePredicateContainer} into a {@link Map} + * + * @return a list of maps representing the contained predicates + */ + private List<Map<String, Object>> containedPredicatesToMaps() { + + List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); + + if (delegate != null) { + org.apache.commons.collections.Predicate[] predicates = delegate.getPredicates(); + + if (predicates != null) { + for (org.apache.commons.collections.Predicate p : predicates) { + if (p instanceof Predicate) { + list.add(((Predicate) p).toMap()); + } else { + throw new UnsupportedOperationException(String.format("Cannot convert a %s to a Map", p.getClass().getName())); + } + } + } + } + + return list; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java new file mode 100644 index 0000000..0e800ea --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/DelegatedSinglePredicateContainer.java @@ -0,0 +1,110 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Collections; +import java.util.Map; + +/** + * DelegatedSinglePredicateContainer is an abstract class providing functionality to managing a + * delegate used to hold a since {@link Predicate}s. For example <code>not</code>. + */ +abstract class DelegatedSinglePredicateContainer extends Predicate implements PredicateDecorator { + + /** + * The delegate {@link PredicateDecorator} used to handle the internal logic for the container + */ + private final PredicateDecorator delegate; + + /** + * Constructor. + * + * @param name the name of this predicate + * @param delegate the delegate used to handle the internal logic for the container + */ + DelegatedSinglePredicateContainer(String name, PredicateDecorator delegate) { + super(name); + this.delegate = delegate; + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(getName(), containedPredicateToMap()); + } + + @Override + public boolean evaluate(Object o) { + return delegate.evaluate(o); + } + + @Override + public org.apache.commons.collections.Predicate[] getPredicates() { + return delegate.getPredicates(); + } + + @Override + public int hashCode() { + return super.hashCode() + ((delegate == null) ? 0 : delegate.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof DelegatedSinglePredicateContainer) && (hashCode() == obj.hashCode())) { + DelegatedSinglePredicateContainer p = (DelegatedSinglePredicateContainer) obj; + return (delegate == null) ? (p.delegate == null) : delegate.equals(p.delegate); + } else { + return false; + } + } + + /** + * Processes the contained predicate into a Map. + * <p> + * This is used to serialize this {@link DelegatedSinglePredicateContainer} into a {@link Map} + * + * @return a map representing the contained predicate + */ + private Map<String, Object> containedPredicateToMap() { + + Map<String, Object> map = null; + + if (delegate != null) { + org.apache.commons.collections.Predicate[] predicates = delegate.getPredicates(); + + if ((predicates != null) && (predicates.length > 0)) { + // Only process the 1st predicate. + org.apache.commons.collections.Predicate p = predicates[0]; + if (p instanceof Predicate) { + map = ((Predicate) p).toMap(); + } else { + throw new UnsupportedOperationException(String.format("Cannot convert a %s to a Map", p.getClass().getName())); + } + } + } + + return map; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java new file mode 100644 index 0000000..a1a8716 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/EqualsPredicate.java @@ -0,0 +1,115 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.functors.EqualPredicate; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +/** + * {@link EqualsPredicate} wraps {@link org.apache.commons.collections.functors.EqualPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data as well + * as obtaining data using a {@link ContextTransformer} + */ +public class EqualsPredicate extends OperationPredicate { + + /** + * The name of this {@link Predicate} implementation + */ + public static final String NAME = "equals"; + + /** + * The {@link org.apache.commons.collections.functors.EqualPredicate} to delegate operations to + */ + private final org.apache.commons.collections.functors.EqualPredicate delegate; + + /** + * Creates a new {@link EqualsPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "equals" and the value is a {@link Collection} of 2 {@link String}s. The first value + * should be the key to use when retrieving data from the context, which should resolve to a + * {@link String} (or null). The second value should be the value to use when checking for equality. + * + * @return a new {@link EqualsPredicate} + */ + public static EqualsPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof String) && (d2 instanceof String)) { + return new EqualsPredicate(new ContextTransformer((String) d1), (String) d2); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor. + * + * @param transformer the {@link ContextTransformer} + * @param value the value to test + */ + public EqualsPredicate(ContextTransformer transformer, String value) { + super(NAME, transformer); + delegate = new EqualPredicate(value); + } + + /** + * Gets the test value + * + * @return a string + */ + public String getValue() { + Object o = (delegate == null) ? null : delegate.getValue(); + return (o == null) ? null : o.toString(); + } + + @Override + public Map<String, Object> toMap() { + return Collections.<String, Object>singletonMap(NAME, + new ArrayList<String>(Arrays.asList(getContextKey(), delegate.getValue().toString()))); + } + + @Override + public boolean evaluateTransformedData(Object data) { + return delegate.evaluate(data); + } +} + http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java new file mode 100644 index 0000000..64dc592 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/NotPredicate.java @@ -0,0 +1,80 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.PredicateUtils; +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Map; + +/** + * {@link NotPredicate} wraps {@link org.apache.commons.collections.functors.NotPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + * <p> + * See {@link DelegatedSinglePredicateContainer} + */ +public class NotPredicate extends DelegatedSinglePredicateContainer { + + /** + * The name of this {@link org.apache.ambari.server.collections.Predicate} implementation + */ + public static final String NAME = "not"; + + /** + * Creates a new {@link NotPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "not" and the value is a {@link Map} representing the contained predicate. + * + * @return a new {@link NotPredicate} + */ + public static NotPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Map) { + return new NotPredicate(PredicateUtils.fromMap((Map) data)); + } else { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } + } + + /** + * Constructor. + * + * @param predicate the predicate to negate + */ + public NotPredicate(Predicate predicate) { + super(NAME, + (PredicateDecorator) org.apache.commons.collections.functors.NotPredicate.getInstance(predicate)); + } + + @Override + public int hashCode() { + return super.hashCode() + this.getClass().getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || + ((obj != null) && (super.equals(obj) && this.getClass().isInstance(obj) && (hashCode() == obj.hashCode()))); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java new file mode 100644 index 0000000..c57f7c2 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OperationPredicate.java @@ -0,0 +1,112 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.apache.commons.collections.Transformer; + +/** + * OperationPredicate is an abstract class providing functionality of transforming the input context + * before executing the implementation-specific + * {@link org.apache.commons.collections.Predicate#evaluate(Object)} method. + */ +abstract class OperationPredicate extends Predicate { + /** + * The {@link Transformer} to use to transform the data before evaluation + */ + private ContextTransformer transformer = null; + + /** + * Constructor + * + * @param name the name of this {@link Predicate} + * @param transformer the {@link Transformer} to use to transform the data before evaluation + */ + OperationPredicate(String name, ContextTransformer transformer) { + super(name); + this.transformer = transformer; + } + + /** + * Gets the {@link Transformer} + * + * @return the assigned {@link ContextTransformer} + */ + public ContextTransformer getTransformer() { + return transformer; + } + + /** + * Sets the {@link Transformer} + * + * @param transformer a {@link ContextTransformer} + */ + public void setTransformer(ContextTransformer transformer) { + this.transformer = transformer; + } + + + /** + * Gets the context key assigned to the {@link ContextTransformer}. + * <p> + * This key is used to identify which value from the context a passed to the + * {@link org.apache.commons.collections.Predicate#evaluate(Object)} method + * + * @return a key name + */ + public String getContextKey() { + return (this.transformer == null) ? null : this.transformer.getKey(); + } + + @Override + public boolean evaluate(Object o) { + Object data = (transformer == null) ? o : transformer.transform(o); + return evaluateTransformedData(data); + } + + @Override + public int hashCode() { + return super.hashCode() + (37 * ((transformer == null) ? 0 : transformer.hashCode())); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null) { + return false; + } else if (super.equals(obj) && (obj instanceof OperationPredicate) && (hashCode() == obj.hashCode())) { + OperationPredicate p = (OperationPredicate) obj; + return (transformer == null) ? (p.transformer == null) : transformer.equals(p.transformer); + } else { + return false; + } + } + + /** + * Abstract method used internally to process the {@link #evaluate(Object)} logic after the + * input data is transformed. + * + * @param data the transformed data to use + * @return the result of the evaluation (<code>true</code>, <code>false</code>) + * @see org.apache.commons.collections.Predicate#evaluate(Object) + */ + protected abstract boolean evaluateTransformedData(Object data); + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.java new file mode 100644 index 0000000..b207e40 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/OrPredicate.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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.PredicateUtils; +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.functors.PredicateDecorator; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +/** + * {@link OrPredicate} wraps {@link org.apache.commons.collections.functors.OrPredicate} to + * provide additional functionality like serializing to and from a Map and JSON formatted data. + * <p> + * See {@link DelegatedMultiplePredicateContainer} + */ +public class OrPredicate extends DelegatedMultiplePredicateContainer { + /** + * The name of this {@link org.apache.ambari.server.collections.Predicate} implementation + */ + public static final String NAME = "or"; + + /** + * Creates a new {@link OrPredicate} using the given {@link Map} of data. + * <p> + * It is expected that the map contains a single {@link java.util.Map.Entry} where the key name + * is "or" and the value is a {@link Collection} of {@link Map}s representing the contained + * predicates. + * + * @return a new {@link OrPredicate} + */ + public static OrPredicate fromMap(Map<String, Object> map) { + Object data = (map == null) ? null : map.get(NAME); + + if (data == null) { + throw new IllegalArgumentException("Missing data for '" + NAME + "' operation"); + } else if (data instanceof Collection) { + Collection<?> collection = (Collection) data; + if (collection.size() == 2) { + Iterator<?> iterator = collection.iterator(); + Object d1 = iterator.next(); + Object d2 = iterator.next(); + + if ((d1 instanceof Map) && (d2 instanceof Map)) { + return new OrPredicate(PredicateUtils.fromMap((Map) d1), PredicateUtils.fromMap((Map) d2)); + } else { + throw new IllegalArgumentException(String.format("Unexpected data types for predicates: %s and %s", d1.getClass().getName(), d2.getClass().getName())); + } + } else { + throw new IllegalArgumentException(String.format("Missing data for '" + NAME + "' operation - 2 predicates are needed, %d found", collection.size())); + } + } else { + throw new IllegalArgumentException(String.format("Unexpected data type for '" + NAME + "' operation - %s", data.getClass().getName())); + } + } + + /** + * Constructor. + * + * @param predicate1 the first predicate to process + * @param predicate2 the second predicate to process (if the first one yields <code>false</code> + */ + public OrPredicate(Predicate predicate1, Predicate predicate2) { + super(NAME, + (PredicateDecorator) org.apache.commons.collections.functors.OrPredicate.getInstance(predicate1, predicate2)); + } + + @Override + public int hashCode() { + return super.hashCode() + this.getClass().getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return (obj == this) || + ((obj != null) && (super.equals(obj) && this.getClass().isInstance(obj) && (hashCode() == obj.hashCode()))); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java new file mode 100644 index 0000000..f1f85c0 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/collections/functors/PredicateClassFactory.java @@ -0,0 +1,58 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * {@link PredicateClassFactory} is a factory class used to derive a {@link Predicate} implementation + * class from its name. + */ +public class PredicateClassFactory { + /** + * A static map of names to {@link Class}s. + */ + private static final Map<String, Class<? extends Predicate>> NAME_TO_CLASS; + + static { + Map<String, Class<? extends Predicate>> map = new HashMap<String, Class<? extends Predicate>>(); + + map.put(AndPredicate.NAME, AndPredicate.class); + map.put(OrPredicate.NAME, OrPredicate.class); + map.put(NotPredicate.NAME, NotPredicate.class); + map.put(ContainsPredicate.NAME, ContainsPredicate.class); + map.put(EqualsPredicate.NAME, EqualsPredicate.class); + + NAME_TO_CLASS = Collections.unmodifiableMap(map); + } + + /** + * Return a {@link Predicate} implementation class give its name + * + * @param name the name of a {@link Predicate} implementation + * @return a {@link Predicate} implementation class give its name or <code>null</code> is not found + */ + public static Class<? extends Predicate> getPredicateClass(String name) { + return (name == null) ? null : NAME_TO_CLASS.get(name); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java new file mode 100644 index 0000000..b97212d --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/PredicateUtilsTest.java @@ -0,0 +1,143 @@ +/* + * 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.ambari.server.collections; + +import junit.framework.Assert; +import org.apache.ambari.server.collections.functors.AndPredicate; +import org.apache.ambari.server.collections.functors.ContainsPredicate; +import org.apache.ambari.server.collections.functors.ContextTransformer; +import org.apache.ambari.server.collections.functors.EqualsPredicate; +import org.apache.ambari.server.collections.functors.NotPredicate; +import org.apache.ambari.server.collections.functors.OrPredicate; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +public class PredicateUtilsTest { + @Test + public void toMap() throws Exception { + // Test null predicate safely returns null + Assert.assertNull(PredicateUtils.toMap(null)); + + Assert.assertEquals(createMap(), PredicateUtils.toMap(createPredicate())); + } + + @Test + public void fromMap() throws Exception { + verifyPredicate(PredicateUtils.fromMap(createMap())); + } + + @Test + public void toJSON() throws Exception { + // Test null predicate safely returns null + Assert.assertNull(PredicateUtils.toJSON(null)); + + Assert.assertEquals(createJSON(), PredicateUtils.toJSON(createPredicate())); + } + + @Test + public void fromJSON() throws Exception { + verifyPredicate(PredicateUtils.fromJSON(createJSON())); + } + + private Predicate createPredicate() { + ContextTransformer transformer1 = new ContextTransformer("services"); + ContextTransformer transformer2 = new ContextTransformer("configurations/service-env/property1"); + ContextTransformer transformer3 = new ContextTransformer("configurations/cluster-env/property1"); + ContainsPredicate predicate1 = new ContainsPredicate(transformer1, "HDFS"); + EqualsPredicate predicate2 = new EqualsPredicate(transformer2, "true"); + EqualsPredicate predicate3 = new EqualsPredicate(transformer3, "false"); + + AndPredicate andPredicate = new AndPredicate(predicate1, predicate2); + OrPredicate orPredicate = new OrPredicate(predicate3, andPredicate); + + return new NotPredicate(orPredicate); + } + + private Map<String, Object> createMap() { + Map<String, Object> andMap = + Collections.<String, Object>singletonMap( + AndPredicate.NAME, Arrays.asList( + Collections.<String, Object>singletonMap(ContainsPredicate.NAME, Arrays.asList("services", "HDFS")), + Collections.<String, Object>singletonMap(EqualsPredicate.NAME, Arrays.asList("configurations/service-env/property1", "true")) + ) + ); + + Map<String, Object> orMap = + Collections.<String, Object>singletonMap(OrPredicate.NAME, + Arrays.asList( + Collections.<String, Object>singletonMap(EqualsPredicate.NAME, Arrays.asList("configurations/cluster-env/property1", "false")), + andMap + ) + ); + + return Collections.<String, Object>singletonMap(NotPredicate.NAME, orMap); + } + + private String createJSON() { + String andJSON = "{\"and\":[{\"contains\":[\"services\",\"HDFS\"]},{\"equals\":[\"configurations/service-env/property1\",\"true\"]}]}"; + String orJSON = "{\"or\":[{\"equals\":[\"configurations/cluster-env/property1\",\"false\"]}," + andJSON + "]}"; + return "{\"not\":" + orJSON + "}"; + } + + private void verifyPredicate(Predicate predicate) { + Assert.assertNotNull(predicate); + Assert.assertEquals(NotPredicate.NAME, predicate.getName()); + Assert.assertTrue(predicate instanceof NotPredicate); + + org.apache.commons.collections.Predicate[] predicates; + + predicates = ((NotPredicate) predicate).getPredicates(); + Assert.assertEquals(1, predicates.length); + + Assert.assertNotNull(predicates[0]); + Assert.assertTrue(predicates[0] instanceof OrPredicate); + Assert.assertEquals(OrPredicate.NAME, ((OrPredicate) predicates[0]).getName()); + + predicates = ((OrPredicate) predicates[0]).getPredicates(); + Assert.assertEquals(2, predicates.length); + + Assert.assertNotNull(predicates[0]); + Assert.assertTrue(predicates[0] instanceof EqualsPredicate); + Assert.assertEquals(EqualsPredicate.NAME, ((EqualsPredicate) predicates[0]).getName()); + Assert.assertEquals("configurations/cluster-env/property1", ((EqualsPredicate) predicates[0]).getContextKey()); + Assert.assertEquals("false", ((EqualsPredicate) predicates[0]).getValue()); + + Assert.assertNotNull(predicates[1]); + Assert.assertTrue(predicates[1] instanceof AndPredicate); + Assert.assertEquals(AndPredicate.NAME, ((AndPredicate) predicates[1]).getName()); + + predicates = ((AndPredicate) predicates[1]).getPredicates(); + Assert.assertEquals(2, predicates.length); + + Assert.assertNotNull(predicates[0]); + Assert.assertTrue(predicates[0] instanceof ContainsPredicate); + Assert.assertEquals(ContainsPredicate.NAME, ((ContainsPredicate) predicates[0]).getName()); + Assert.assertEquals("services", ((ContainsPredicate) predicates[0]).getContextKey()); + Assert.assertEquals("HDFS", ((ContainsPredicate) predicates[0]).getValue()); + + Assert.assertNotNull(predicates[1]); + Assert.assertTrue(predicates[1] instanceof EqualsPredicate); + Assert.assertEquals(EqualsPredicate.NAME, ((EqualsPredicate) predicates[1]).getName()); + Assert.assertEquals("configurations/service-env/property1", ((EqualsPredicate) predicates[1]).getContextKey()); + Assert.assertEquals("true", ((EqualsPredicate) predicates[1]).getValue()); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.java new file mode 100644 index 0000000..977fe9d --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/AndPredicateTest.java @@ -0,0 +1,108 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.easymock.EasyMock.expect; + +public class AndPredicateTest extends EasyMockSupport { + + @Test + public void testEvaluate() { + Predicate mockPredicate1 = createStrictMock(Predicate.class); + expect(mockPredicate1.evaluate("context")).andReturn(true).times(1); + expect(mockPredicate1.evaluate("context")).andReturn(false).times(1); + expect(mockPredicate1.evaluate("context")).andReturn(true).times(1); + + Predicate mockPredicate2 = createStrictMock(Predicate.class); + expect(mockPredicate2.evaluate("context")).andReturn(true).times(1); + expect(mockPredicate2.evaluate("context")).andReturn(true).times(1); + + replayAll(); + + AndPredicate predicate = new AndPredicate(mockPredicate1, mockPredicate2); + + // Try with true and true + predicate.evaluate("context"); + + // Try with false and ???? (short circuited) + predicate.evaluate("context"); + + // Try with true and false + predicate.evaluate("context"); + + verifyAll(); + + Assert.assertArrayEquals(new Predicate[]{mockPredicate1, mockPredicate2}, predicate.getPredicates()); + } + + @Test + public void testToMap() { + Predicate mockPredicate1 = createStrictMock(Predicate.class); + expect(mockPredicate1.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "foo")).times(1); + + Predicate mockPredicate2 = createStrictMock(Predicate.class); + expect(mockPredicate2.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "baz")).times(1); + + replayAll(); + + AndPredicate predicate = new AndPredicate(mockPredicate1, mockPredicate2); + Map<String, Object> actualMap = predicate.toMap(); + + verifyAll(); + + Map<String, Object> expectedMap = new HashMap<String, Object>(); + expectedMap.put("and", new ArrayList<Map<String, Object>>( + Arrays.asList(Collections.<String, Object>singletonMap("nop", "foo"), + Collections.<String, Object>singletonMap("nop", "baz")) + )); + + Assert.assertEquals(expectedMap, actualMap); + } + + @Test + public void testToJSON() { + Predicate mockPredicate1 = createStrictMock(Predicate.class); + expect(mockPredicate1.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "foo")).times(1); + + Predicate mockPredicate2 = createStrictMock(Predicate.class); + expect(mockPredicate2.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "baz")).times(1); + + replayAll(); + + AndPredicate predicate = new AndPredicate(mockPredicate1, mockPredicate2); + String actualJSON = predicate.toJSON(); + + verifyAll(); + + String expectedJSON = "{\"and\":[{\"nop\":\"foo\"},{\"nop\":\"baz\"}]}"; + + Assert.assertEquals(expectedJSON, actualJSON); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java new file mode 100644 index 0000000..d27510f --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContainsPredicateTest.java @@ -0,0 +1,91 @@ +/* + * 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.ambari.server.collections.functors; + +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.expect; + +public class ContainsPredicateTest extends EasyMockSupport { + + @Test + public void testEvaluate() { + Set<String> data1 = new HashSet<String>(Arrays.asList("ONE", "TWO", "THREE")); + Set<String> data2 = new HashSet<String>(Arrays.asList("TWO", "THREE")); + + ContextTransformer transformer = createStrictMock(ContextTransformer.class); + expect(transformer.transform(anyObject(Map.class))).andReturn(data1).times(1); + expect(transformer.transform(anyObject(Map.class))).andReturn(data2).times(1); + + replayAll(); + + ContainsPredicate predicate = new ContainsPredicate(transformer, "ONE"); + + Assert.assertTrue(predicate.evaluate(Collections.singletonMap("data", data1))); + Assert.assertFalse(predicate.evaluate(Collections.singletonMap("data", data2))); + + verifyAll(); + } + + @Test + public void testToMap() { + ContextTransformer transformer = createStrictMock(ContextTransformer.class); + expect(transformer.getKey()).andReturn("data").times(1); + + replayAll(); + + ContainsPredicate predicate = new ContainsPredicate(transformer, "ONE"); + Map<String, Object> actualMap = predicate.toMap(); + + verifyAll(); + + Map<String, Object> expectedMap = new HashMap<String, Object>(); + expectedMap.put("contains", new ArrayList<String>(Arrays.asList("data", "ONE"))); + + Assert.assertEquals(expectedMap, actualMap); + } + + @Test + public void testToJSON() { + ContextTransformer transformer = createStrictMock(ContextTransformer.class); + expect(transformer.getKey()).andReturn("data").times(1); + + replayAll(); + + ContainsPredicate predicate = new ContainsPredicate(transformer, "ONE"); + String actualJSON = predicate.toJSON(); + + verifyAll(); + + String expectedJSON = "{\"contains\":[\"data\",\"ONE\"]}"; + + Assert.assertEquals(expectedJSON, actualJSON); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java new file mode 100644 index 0000000..da77b35 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/ContextTransformerTest.java @@ -0,0 +1,75 @@ +/* + * 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.ambari.server.collections.functors; + + +import junit.framework.Assert; +import org.easymock.EasyMockSupport; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class ContextTransformerTest extends EasyMockSupport { + + @Test + public void testGetKey() { + ContextTransformer transformer = new ContextTransformer("key"); + Assert.assertEquals("key", transformer.getKey()); + } + + @Test + public void testTransformSimple() { + Map<String, Object> context = new HashMap<String, Object>(); + context.put("key", "value"); + context.put("key1", "value1"); + context.put("key2", "value2"); + + ContextTransformer transformer = new ContextTransformer("key"); + Assert.assertEquals("value", transformer.transform(context)); + } + + @Test + public void testTransformTree() { + Map<String, Object> serviceSite = new HashMap<String, Object>(); + serviceSite.put("property", "service-site-property"); + + Map<String, Object> configurations = new HashMap<String, Object>(); + configurations.put("service-site", serviceSite); + configurations.put("property", "configuration-property"); + + Map<String, Object> context = new HashMap<String, Object>(); + context.put("configurations", configurations); + context.put("property", "context-property"); + + ContextTransformer transformer; + + // Without leading "/" + transformer = new ContextTransformer("configurations/service-site/property"); + Assert.assertEquals("service-site-property", transformer.transform(context)); + + // With leading "/" + transformer = new ContextTransformer("/configurations/service-site/property"); + Assert.assertEquals("service-site-property", transformer.transform(context)); + + // Get map of properties + transformer = new ContextTransformer("/configurations/service-site"); + Assert.assertEquals(serviceSite, transformer.transform(context)); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java new file mode 100644 index 0000000..0ea4454 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/EqualsPredicateTest.java @@ -0,0 +1,89 @@ +/* + * 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.ambari.server.collections.functors; + +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.expect; + +public class EqualsPredicateTest extends EasyMockSupport { + + @Test + public void testEvaluate() { + String data1 = "value1"; + String data2 = "value2"; + + ContextTransformer transformer = createStrictMock(ContextTransformer.class); + expect(transformer.transform(anyObject(Map.class))).andReturn(data1).times(1); + expect(transformer.transform(anyObject(Map.class))).andReturn(data2).times(1); + + replayAll(); + + EqualsPredicate predicate = new EqualsPredicate(transformer, "value1"); + + Assert.assertTrue(predicate.evaluate(Collections.singletonMap("data", data1))); + Assert.assertFalse(predicate.evaluate(Collections.singletonMap("data", data2))); + + verifyAll(); + } + + @Test + public void testToMap() { + ContextTransformer transformer = createStrictMock(ContextTransformer.class); + expect(transformer.getKey()).andReturn("data").times(1); + + replayAll(); + + EqualsPredicate predicate = new EqualsPredicate(transformer, "value"); + Map<String, Object> actualMap = predicate.toMap(); + + verifyAll(); + + Map<String, Object> expectedMap = new HashMap<String, Object>(); + expectedMap.put("equals", new ArrayList<String>(Arrays.asList("data", "value"))); + + Assert.assertEquals(expectedMap, actualMap); + } + + @Test + public void testToJSON() { + ContextTransformer transformer = createStrictMock(ContextTransformer.class); + expect(transformer.getKey()).andReturn("data").times(1); + + replayAll(); + + EqualsPredicate predicate = new EqualsPredicate(transformer, "value"); + String actualJSON = predicate.toJSON(); + + verifyAll(); + + String expectedJSON = "{\"equals\":[\"data\",\"value\"]}"; + + Assert.assertEquals(expectedJSON, actualJSON); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java new file mode 100644 index 0000000..f0b8ae3 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/NotPredicateTest.java @@ -0,0 +1,89 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.easymock.EasyMock.expect; + +public class NotPredicateTest extends EasyMockSupport { + + @Test + public void testEvaluate() { + Predicate mockPredicate = createStrictMock(Predicate.class); + expect(mockPredicate.evaluate("context")).andReturn(true).times(1); + expect(mockPredicate.evaluate("context")).andReturn(false).times(1); + + replayAll(); + + NotPredicate predicate = new NotPredicate(mockPredicate); + + // Try with true + Assert.assertFalse(predicate.evaluate("context")); + + // Try with false + Assert.assertTrue(predicate.evaluate("context")); + + verifyAll(); + + Assert.assertArrayEquals(new Predicate[]{mockPredicate}, predicate.getPredicates()); + } + + @Test + public void testToMap() { + Predicate mockPredicate = createStrictMock(Predicate.class); + expect(mockPredicate.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "foo")).times(1); + + replayAll(); + + NotPredicate predicate = new NotPredicate(mockPredicate); + Map<String, Object> actualMap = predicate.toMap(); + + verifyAll(); + + Map<String, Object> expectedMap = new HashMap<String, Object>(); + expectedMap.put("not", Collections.<String, Object>singletonMap("nop", "foo")); + + Assert.assertEquals(expectedMap, actualMap); + } + + @Test + public void testToJSON() { + Predicate mockPredicate = createStrictMock(Predicate.class); + expect(mockPredicate.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "foo")).times(1); + + replayAll(); + + NotPredicate predicate = new NotPredicate(mockPredicate); + String actualJSON = predicate.toJSON(); + + verifyAll(); + + String expectedJSON = "{\"not\":{\"nop\":\"foo\"}}"; + + Assert.assertEquals(expectedJSON, actualJSON); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/198f5880/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java new file mode 100644 index 0000000..7e0bf1b --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/collections/functors/OrPredicateTest.java @@ -0,0 +1,107 @@ +/* + * 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.ambari.server.collections.functors; + +import org.apache.ambari.server.collections.Predicate; +import org.easymock.EasyMockSupport; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.easymock.EasyMock.expect; + +public class OrPredicateTest extends EasyMockSupport { + + @Test + public void testEvaluate() { + Predicate mockPredicate1 = createStrictMock(Predicate.class); + expect(mockPredicate1.evaluate("context")).andReturn(true).times(1); + expect(mockPredicate1.evaluate("context")).andReturn(false).times(1); + expect(mockPredicate1.evaluate("context")).andReturn(true).times(1); + + Predicate mockPredicate2 = createStrictMock(Predicate.class); + expect(mockPredicate2.evaluate("context")).andReturn(true).times(1); + + replayAll(); + + OrPredicate predicate = new OrPredicate(mockPredicate1, mockPredicate2); + + // Try with true and ???? (short circuited) + predicate.evaluate("context"); + + // Try with false and true + predicate.evaluate("context"); + + // Try with true and ???? (short circuited) + predicate.evaluate("context"); + + verifyAll(); + + Assert.assertArrayEquals(new Predicate[]{mockPredicate1, mockPredicate2}, predicate.getPredicates()); + } + + @Test + public void testToMap() { + Predicate mockPredicate1 = createStrictMock(Predicate.class); + expect(mockPredicate1.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "foo")).times(1); + + Predicate mockPredicate2 = createStrictMock(Predicate.class); + expect(mockPredicate2.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "baz")).times(1); + + replayAll(); + + OrPredicate predicate = new OrPredicate(mockPredicate1, mockPredicate2); + Map<String, Object> actualMap = predicate.toMap(); + + verifyAll(); + + Map<String, Object> expectedMap = new HashMap<String, Object>(); + expectedMap.put("or", new ArrayList<Map<String, Object>>( + Arrays.asList(Collections.<String, Object>singletonMap("nop", "foo"), + Collections.<String, Object>singletonMap("nop", "baz")) + )); + + Assert.assertEquals(expectedMap, actualMap); + } + + @Test + public void testToJSON() { + Predicate mockPredicate1 = createStrictMock(Predicate.class); + expect(mockPredicate1.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "foo")).times(1); + + Predicate mockPredicate2 = createStrictMock(Predicate.class); + expect(mockPredicate2.toMap()).andReturn(Collections.<String, Object>singletonMap("nop", "baz")).times(1); + + replayAll(); + + OrPredicate predicate = new OrPredicate(mockPredicate1, mockPredicate2); + String actualJSON = predicate.toJSON(); + + verifyAll(); + + String expectedJSON = "{\"or\":[{\"nop\":\"foo\"},{\"nop\":\"baz\"}]}"; + + Assert.assertEquals(expectedJSON, actualJSON); + } +} \ No newline at end of file
