package org.jolokia.converter.json.simplifier;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import javax.management.AttributeNotFoundException;
import org.jolokia.converter.json.*;
import org.jolokia.converter.object.StringToObjectConverter;
import org.json.simple.JSONObject;
/*
* Copyright 2009-2013 Roland Huss
*
* Licensed 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.
*/
/**
* Base class for all simplifiers. A simplifier is a special {@link Extractor} which
* condense full blown Java beans (like {@link java.io.File}) to a more compact representation.
* Simplifier extractors cannot be written to and are only used for downstream serialization.
*
* Simplifier are registered by listing the classes in a <code>META-INF/simplifiers</code> plain text file and
* then picked up by the converter. The default simplifiers coming prepackaged are taken from
* <code>META-INF/simplifiers-default</code>
*
* @author roland
* @since Jul 27, 2009
*/
public abstract class SimplifierExtractor<T> implements Extractor {
private final Map<String, AttributeExtractor<T>> extractorMap;
private Class<T> type;
/**
* Super constructor taking the value type as argument
*
* @param pType type for which this extractor is responsible
*/
protected SimplifierExtractor(Class<T> pType) {
extractorMap = new HashMap<String, AttributeExtractor<T>>();
type = pType;
// Old method, here only for backwards compatibility. Please initialize in the constructor instead
init(extractorMap);
}
/** {@inheritDoc} */
public Class getType() {
return type;
}
/** {@inheritDoc} */
public Object extractObject(ObjectToJsonConverter pConverter, Object pValue, Stack<String> pPathParts, boolean jsonify)
throws AttributeNotFoundException {
String path = pPathParts.isEmpty() ? null : pPathParts.pop();
ValueFaultHandler faultHandler = pConverter.getValueFaultHandler();
if (path != null) {
return extractWithPath(pConverter, pValue, pPathParts, jsonify, path, faultHandler);
} else {
return jsonify ? extractAll(pConverter, (T) pValue, pPathParts, jsonify) : pValue;
}
}
private Object extractAll(ObjectToJsonConverter pConverter, T pValue, Stack<String> pPathParts, boolean jsonify) throws AttributeNotFoundException {
JSONObject ret = new JSONObject();
for (Map.Entry<String, AttributeExtractor<T>> entry : extractorMap.entrySet()) {
Stack<String> paths = (Stack<String>) pPathParts.clone();
try {
Object value = entry.getValue().extract(pValue);
ret.put(entry.getKey(),pConverter.extractObject(value, paths, jsonify));
} catch (AttributeExtractor.SkipAttributeException e) {
// Skip this one ...
continue;
} catch (ValueFaultHandler.AttributeFilteredException e) {
// ... and this, too
continue;
}
}
if (ret.isEmpty()) {
// Everything filtered, bubble up ...
throw new ValueFaultHandler.AttributeFilteredException();
}
return ret;
}
private Object extractWithPath(ObjectToJsonConverter pConverter, Object pValue, Stack<String> pPathParts, boolean jsonify, String pPath, ValueFaultHandler pFaultHandler) throws AttributeNotFoundException {
AttributeExtractor<T> extractor = extractorMap.get(pPath);
if (extractor == null) {
return pFaultHandler.handleException(new AttributeNotFoundException("Illegal path element " + pPath + " for object " + pValue));
}
try {
Object attributeValue = extractor.extract((T) pValue);
return pConverter.extractObject(attributeValue, pPathParts, jsonify);
} catch (AttributeExtractor.SkipAttributeException e) {
return pFaultHandler.handleException(new AttributeNotFoundException("Illegal path element " + pPath + " for object " + pValue));
}
}
/**
* No setting for simplifying extractors
* @return always <code>false</code>
*/
public boolean canSetValue() {
return false;
}
/**
* Throws always {@link IllegalArgumentException} since a simplifier cannot be written to
*/
public Object setObjectValue(StringToObjectConverter pConverter, Object pInner,
String pAttribute, Object pValue) throws IllegalAccessException, InvocationTargetException {
// never called
throw new IllegalArgumentException("A simplify handler can't set a value");
}
/**
* Add given extractors to the map. Should be called by a subclass from within init()
*
* @param pAttrExtractors extractors
*/
@SuppressWarnings("unchecked")
protected final void addExtractors(Object[][] pAttrExtractors) {
for (Object[] pAttrExtractor : pAttrExtractors) {
extractorMap.put((String) pAttrExtractor[0],
(AttributeExtractor<T>) pAttrExtractor[1]);
}
}
/**
* Add a single extractor
* @param pName name of the extractor
* @param pExtractor the extractor itself
*/
protected final void addExtractor(String pName, AttributeExtractor<T> pExtractor) {
extractorMap.put(pName,pExtractor);
}
// ============================================================================
/**
* Helper interface for extracting and simplifying values
*
* @param <T> type to extract
*/
public interface AttributeExtractor<T> {
/**
* Exception to be thrown when the result of this extractor should be omitted in the response
*/
class SkipAttributeException extends Exception {}
/**
* Extract the real value from a given value
* @param value to extract from
* @return the extracted value
* @throws SkipAttributeException if this value which is about to be extracted
* should be omitted in the result
*/
Object extract(T value) throws SkipAttributeException;
}
/**
* Add extractors to map
*
* @deprecated Initialize in the constructor instead.
* @param pExtractorMap the map to add the extractors used within this simplifier
*/
void init(Map<String, AttributeExtractor<T>> pExtractorMap) {}
}