/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package com.bc.ceres.jai.opimage;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import javax.media.jai.EnumeratedParameter;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.ParameterListDescriptor;
import javax.media.jai.RenderedOp;
import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.awt.image.renderable.RenderedImageFactory;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @see com.sun.media.jai.opimage.ConvolveOpImage
*/
public class XmlRIF implements RenderedImageFactory {
private static final String ENAME_PARAMETER = "parameter";
private static final String ENAME_SOURCE = "source";
private static final String ENAME_TARGET = "target";
private static final String ENAME_OP = "op";
private static final String ANAME_ID = "id";
private static final String ANAME_REFID = "refid";
/**
* Constructor.
*/
public XmlRIF() {
}
/**
* Create a new instance of ConvolveOpImage in the rendered layer.
* This method satisfies the implementation of RIF.
*
* @param paramBlock The source image and the convolution kernel.
*/
@Override
public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderingHints) {
URI location = (URI) paramBlock.getObjectParameter(0);
Map<String, Object> configuration = (Map<String, Object>) paramBlock.getObjectParameter(1);
if (configuration == null) {
configuration = new HashMap<String, Object>();
}
try {
return create(location, configuration, renderingHints);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
private RenderedImage create(URI location, Map<String, Object> configuration, RenderingHints renderingHints) throws JDOMException, IOException,
IllegalArgumentException {
configuration = new HashMap<String, Object>(configuration);
SAXBuilder builder = new SAXBuilder();
Document document = builder.build(location.toURL());
Element rootElement = document.getRootElement();
Map<String, Element> sourceMap = getElementMap(rootElement, ENAME_SOURCE);
Map<String, Element> parameterMap = getElementMap(rootElement, ENAME_PARAMETER);
Element targetElement = rootElement.getChild(ENAME_TARGET);
return parseImage(targetElement, sourceMap, parameterMap, configuration, renderingHints, "rendered");
}
private RenderedOp parseImage(Element targetElement, Map<String, Element> definedSourceElements, Map<String, Element> definedParameterElements,
Map<String, Object> configuration, RenderingHints renderHints, String modeName) {
Element opElement = targetElement.getChild(ENAME_OP);
String opName = opElement.getValue();
ParameterBlockJAI parameterBlock = new ParameterBlockJAI(opName, modeName);
parseSources(parameterBlock,
targetElement,
definedSourceElements,
definedParameterElements,
configuration,
renderHints);
parseParameters(parameterBlock,
targetElement,
definedParameterElements,
configuration);
return JAI.create(opName, parameterBlock, renderHints);
}
private void parseSources(ParameterBlockJAI parameterBlock,
Element targetElement,
Map<String, Element> definedSourceElements,
Map<String, Element> definedParameterElements,
Map<String, Object> configuration,
RenderingHints renderingHints) {
List sourceElements = targetElement.getChildren(ENAME_SOURCE);
for (int i = 0; i < sourceElements.size(); i++) {
Element sourceElement = (Element) sourceElements.get(i);
String sourceName = sourceElement.getAttributeValue(ANAME_ID);
String sourceId = sourceElement.getAttributeValue(ANAME_REFID);
Object source;
if (sourceId != null) {
source = configuration.get(sourceId);
if (source == null) {
Element definedSourceElement = definedSourceElements.get(sourceId);
if (definedSourceElement != null) {
source = parseImage(definedSourceElement,
definedSourceElements,
definedParameterElements,
configuration,
renderingHints,
parameterBlock.getMode());
configuration.put(sourceId, source);
}
}
} else {
source = parseImage(sourceElement,
definedSourceElements,
definedParameterElements,
configuration,
renderingHints,
parameterBlock.getMode());
}
if (sourceName != null) {
parameterBlock.setSource(sourceName, source);
} else {
parameterBlock.setSource(source, i);
}
}
}
private void parseParameters(ParameterBlockJAI parameterBlock,
Element targetElement,
Map<String, Element> definedParameterElements,
Map<String, Object> configuration) {
List parameterElements = targetElement.getChildren(ENAME_PARAMETER);
for (int i = 0; i < parameterElements.size(); i++) {
Element parameterElement = (Element) parameterElements.get(i);
String parameterName = parameterElement.getAttributeValue(ANAME_ID);
if (parameterName == null) {
String[] paramNames = parameterBlock.getParameterListDescriptor().getParamNames();
if (i < paramNames.length) {
parameterName = paramNames[i];
} else {
throw new IllegalArgumentException(
MessageFormat.format("Operation ''{0}'': Unknown parameter #{1}'", parameterBlock.getOperationDescriptor().getName(), i));
}
}
String parameterId = parameterElement.getAttributeValue(ANAME_REFID);
Object parameterValue;
if (parameterId != null) {
parameterValue = configuration.get(parameterId);
if (parameterValue == null) {
Element definedParameterElement = definedParameterElements.get(parameterId);
parameterValue = parseParameterValue(parameterBlock,
parameterName,
definedParameterElement.getValue());
configuration.put(parameterId, parameterValue);
}
} else {
parameterValue = parseParameterValue(parameterBlock,
parameterName,
parameterElement.getValue());
}
if (parameterName != null) {
parameterBlock.setParameter(parameterName, parameterValue);
} else {
parameterBlock.add(parameterValue);
}
}
}
private static Map<String, Element> getElementMap(Element rootElement, String elementName) {
Map<String, Element> elementMap = new HashMap<String, Element>();
List elements = rootElement.getChildren(elementName);
for (int i = 0; i < elements.size(); i++) {
Element element = (Element) elements.get(i);
String name = element.getAttributeValue(ANAME_ID);
if (name == null) {
throw new IllegalArgumentException(MessageFormat.format("Missing attribute ''{0}'' in element ''{1}''", ANAME_ID, elementName));
}
elementMap.put(name, element);
}
return elementMap;
}
private Object parseParameterValue(ParameterBlockJAI parameterBlock, String parameterName, String text) {
ParameterListDescriptor descriptor = parameterBlock.getParameterListDescriptor();
int parameterIndex = getParameterIndex(descriptor, parameterName);
if (parameterIndex == -1) {
throw new IllegalArgumentException(
MessageFormat.format("Operation ''{0}'': Unknown parameter ''{1}''", parameterBlock.getOperationDescriptor().getName(),
parameterName));
}
Class[] types = descriptor.getParamClasses();
return parse(parameterBlock.getOperationDescriptor(), types[parameterIndex], text);
}
private int getParameterIndex(ParameterListDescriptor descriptor, String parameterName) {
String[] names = descriptor.getParamNames();
for (int i = 0; i < names.length; i++) {
if (names[i].equalsIgnoreCase(parameterName)) {
return i;
}
}
return -1;
}
private Object parse(OperationDescriptor operationDescriptor, Class type, String text) {
// System.out.println("operationDescriptor: " + operationDescriptor.getName());
// System.out.println(" type = " + type);
// System.out.println(" text = " + text);
if (type.equals(String.class)) {
return text;
} else if (type.equals(Byte.class) || type.equals(Byte.TYPE)) {
return Byte.parseByte(text);
} else if (type.equals(Short.class) || type.equals(Short.TYPE)) {
return Short.parseShort(text);
} else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
return Integer.parseInt(text);
} else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
return Long.parseLong(text);
} else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
return Float.parseFloat(text);
} else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
return Double.parseDouble(text);
} else if (type.equals(int[].class)) {
return parseIntArray(text);
} else if (type.equals(float[].class)) {
return parseFloatArray(text);
} else if (type.equals(double[].class)) {
return parseDoubleArray(text);
} else if (type.equals(Interpolation.class)) {
if ("INTERP_NEAREST".equals(text)) {
return Interpolation.getInstance(Interpolation.INTERP_NEAREST);
} else if ("INTERP_BILINEAR".equals(text)) {
return Interpolation.getInstance(Interpolation.INTERP_BILINEAR);
} else if ("INTERP_BICUBIC".equals(text)) {
return Interpolation.getInstance(Interpolation.INTERP_BICUBIC);
} else if ("INTERP_BICUBIC_2".equals(text)) {
return Interpolation.getInstance(Interpolation.INTERP_BICUBIC_2);
}
throw new IllegalArgumentException("Unknown interpolation method: " + text);
} else if (EnumeratedParameter.class.isAssignableFrom(type)) {
try {
Field field = operationDescriptor.getClass().getField(text);
field.setAccessible(true);
Object value = field.get(operationDescriptor);
return (EnumeratedParameter) value;
} catch (Exception e) {
throw new IllegalArgumentException("Enumerated value not found: " + text);
}
}
throw new IllegalArgumentException("Unhandled type: " + type);
}
private int[] parseIntArray(String text) {
String[] tokens = text.split(",");
int[] value = new int[tokens.length];
for (int i = 0; i < value.length; i++) {
value[i] = Integer.parseInt(tokens[i]);
}
return value;
}
private float[] parseFloatArray(String text) {
String[] tokens = text.split(",");
float[] value = new float[tokens.length];
for (int i = 0; i < value.length; i++) {
value[i] = Float.parseFloat(tokens[i]);
}
return value;
}
private double[] parseDoubleArray(String text) {
String[] tokens = text.split(",");
double[] value = new double[tokens.length];
for (int i = 0; i < value.length; i++) {
value[i] = Double.parseDouble(tokens[i]);
}
return value;
}
}