/*
* 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 java.util.Map;
import org.apache.commons.collections.Transformer;
/**
* {@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;
}
}
}