ingo@16: package de.intevation.flys.client.server;
ingo@16:
ingo@16: import java.util.ArrayList;
ingo@16: import java.util.List;
ingo@16:
ingo@16: import javax.xml.xpath.XPathConstants;
ingo@16:
ingo@16: import org.w3c.dom.Document;
ingo@803: import org.w3c.dom.Element;
ingo@16: import org.w3c.dom.Node;
ingo@16: import org.w3c.dom.NodeList;
ingo@16:
ingo@1367: import org.apache.log4j.Logger;
ingo@1367:
ingo@16: import de.intevation.artifacts.common.ArtifactNamespaceContext;
ingo@16: import de.intevation.artifacts.common.utils.ClientProtocolUtils;
ingo@16: import de.intevation.artifacts.common.utils.XMLUtils;
ingo@16:
ingo@16: import de.intevation.flys.client.shared.model.ArtifactDescription;
ingo@16: import de.intevation.flys.client.shared.model.DataItem;
ingo@51: import de.intevation.flys.client.shared.model.DataList;
ingo@51: import de.intevation.flys.client.shared.model.DefaultArtifactDescription;
ingo@16: import de.intevation.flys.client.shared.model.DefaultData;
ingo@16: import de.intevation.flys.client.shared.model.DefaultDataItem;
ingo@65: import de.intevation.flys.client.shared.model.DefaultOutputMode;
ingo@1595: import de.intevation.flys.client.shared.model.DoubleArrayData;
felix@1593: import de.intevation.flys.client.shared.model.DoubleRangeData;
ingo@1527: import de.intevation.flys.client.shared.model.IntegerArrayData;
ingo@1522: import de.intevation.flys.client.shared.model.IntegerRangeData;
ingo@1527: import de.intevation.flys.client.shared.model.IntegerOptionsData;
ingo@65: import de.intevation.flys.client.shared.model.OutputMode;
ingo@807: import de.intevation.flys.client.shared.model.Recommendation;
ingo@565: import de.intevation.flys.client.shared.model.WQDataItem;
ingo@16:
ingo@16:
ingo@16: /**
ingo@16: * This factory class helps creating an {@link ArtifactDescription} based on the
ingo@16: * DESCRIBE document of an artifact returned by the artifact server. Use the
ingo@16: * {@link createArtifactDescription(org.w3c.dom.Document)} method with the
ingo@16: * DESCRIBE document to create such an {@link ArtifactDescription}.
ingo@16: *
ingo@16: * @author Ingo Weinzierl
ingo@16: */
ingo@16: public class ArtifactDescriptionFactory {
ingo@16:
ingo@1367: private static final Logger logger =
ingo@1367: Logger.getLogger(ArtifactDescriptionFactory.class);
ingo@1367:
ingo@1367:
ingo@16: public static final String XPATH_STATE_NAME = "@art:name";
ingo@16:
ingo@22: public static final String XPATH_UIPROVIDER = "@art:uiprovider";
ingo@22:
ingo@2500: public static final String XPATH_HELP_TEXT = "@art:helpText";
ingo@2500:
ingo@32: public static final String XPATH_REACHABLE_STATE = "art:state";
ingo@32:
ingo@51: public static final String XPATH_STATIC_STATE_NODE = "art:state";
ingo@51:
ingo@34: public static final String XPATH_STATIC_DATA_NODE = "art:data";
ingo@34:
ingo@34: public static final String XPATH_STATIC_ITEM_NODE = "art:item";
ingo@34:
ingo@803: public static final String XPATH_RECOMMENDED_ARTIFACTS =
ingo@808: "/art:result/art:recommended-artifacts//*[@factory]";
ingo@803:
ingo@16: /**
ingo@16: * This method creates the {@link ArtifactDescription} of the DESCRIBE
ingo@16: * document doc.
ingo@16: *
ingo@16: * @param doc A DESCRIBE document.
ingo@16: *
ingo@16: * @return the {@link ArtifactDescription}.
ingo@16: */
ingo@16: public static ArtifactDescription createArtifactDescription(Document doc) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.createArtifactDescription");
ingo@16:
ingo@16: Node currentState = ClientProtocolUtils.getCurrentState(doc);
ingo@16: Node staticNode = ClientProtocolUtils.getStaticUI(doc);
ingo@16: Node dynamicNode = ClientProtocolUtils.getDynamicUI(doc);
ingo@32: Node reachable = ClientProtocolUtils.getReachableStates(doc);
ingo@65: NodeList outputs = ClientProtocolUtils.getOutputModes(doc);
ingo@16:
ingo@16: String state = (String) XMLUtils.xpath(
ingo@16: currentState,
ingo@16: XPATH_STATE_NAME,
ingo@16: XPathConstants.STRING,
ingo@16: ArtifactNamespaceContext.INSTANCE);
ingo@1367:
ingo@1367: logger.debug("Current state name: " + state);
ingo@16:
ingo@51: DataList currentData = extractCurrentData(dynamicNode, state);
ingo@51: DataList[] old = extractOldData(staticNode);
ingo@34: String[] states = extractReachableStates(reachable);
ingo@803: OutputMode[] outs = extractOutputModes(outputs);
ingo@807: Recommendation[] rec = extractRecommendedArtifacts(doc);
ingo@16:
ingo@65: return new DefaultArtifactDescription(
ingo@65: old,
ingo@65: currentData,
ingo@65: state,
ingo@65: states,
ingo@803: outs,
ingo@807: rec);
ingo@16: }
ingo@16:
ingo@16:
ingo@16: /**
ingo@16: * This method extracts the data that the user is able to enter in the
ingo@16: * current state of the artifact.
ingo@16: *
ingo@16: * @param dynamicNode The dynamic node of the DESCRIBE document.
ingo@51: * @param state The name of the current state.
ingo@16: *
ingo@16: * @return A {@link Data} object that represents the data which might be
ingo@16: * entered by the user in the current state or null, if no data might be
ingo@16: * entered.
ingo@16: */
ingo@51: protected static DataList extractCurrentData(Node dynamicNode, String state) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractCurrentData");
ingo@16:
ingo@51: NodeList data = ClientProtocolUtils.getSelectNode(dynamicNode);
ingo@2500: String help = extractHelpText(dynamicNode);
ingo@51: String uiProvider = extractUIProvider(dynamicNode);
ingo@16:
ingo@51: if (data == null || data.getLength() == 0) {
ingo@51: return null;
ingo@51: }
ingo@16:
ingo@1522: int dataNum = data.getLength();
ingo@2500: DataList list = new DataList(state, dataNum, uiProvider, null, help);
ingo@51:
ingo@51: for (int i = 0; i < dataNum; i++) {
ingo@1522: Element d = (Element) data.item(i);
ingo@1522: String label = ClientProtocolUtils.getLabel(d);
ingo@1522: String name = XMLUtils.xpathString(
ingo@1522: d, "@art:name", ArtifactNamespaceContext.INSTANCE);
ingo@1522: String type = XMLUtils.xpathString(
ingo@1522: d, "@art:type", ArtifactNamespaceContext.INSTANCE);
ingo@51:
ingo@1522: logger.debug("Create new IntegerRangeData object for: " + name);
ingo@1522: logger.debug("New Data is from type: " + type);
ingo@51:
felix@1592: // TODO replace with DataFactory.
felix@1592:
ingo@1522: if (type == null || type.length() == 0) {
ingo@1522: NodeList choices = ClientProtocolUtils.getItemNodes(d);
ingo@1522: DataItem[] dataItems = extractCurrentDataItems(choices);
ingo@1522: DataItem def = extractDefaultDataItem(d);
ingo@51:
ingo@1522: list.add(new DefaultData(name, label, null, dataItems, def));
ingo@1522: }
ingo@1522: else if (type.equals("intrange")) {
ingo@1566: String min = ClientProtocolUtils.getMinNode(d);
ingo@1566: String max = ClientProtocolUtils.getMaxNode(d);
ingo@1566:
ingo@1566: String defMin = ClientProtocolUtils.getDefMin(d);
ingo@1566: String defMax = ClientProtocolUtils.getDefMax(d);
ingo@1522:
ingo@1522: try {
ingo@1522: int lower = Integer.parseInt(min);
ingo@1522: int upper = Integer.parseInt(max);
ingo@1566:
ingo@1566: if (defMin != null && defMax != null) {
ingo@1566: list.add(new IntegerRangeData(
ingo@1566: name, label,
ingo@1566: lower, upper,
ingo@1566: Integer.parseInt(defMin),
ingo@1566: Integer.parseInt(defMax)));
ingo@1566: }
ingo@1566: else {
ingo@1566: list.add(
ingo@1566: new IntegerRangeData(name, label, lower, upper));
ingo@1566: }
ingo@1522: }
ingo@1522: catch (NumberFormatException nfe) {
ingo@1522: logger.warn("NumberFormatException: ", nfe);
ingo@1522: }
ingo@1522: }
ingo@1527: else if (type.equals("intarray")) {
ingo@1527: list.add(new IntegerArrayData(name, label, null));
ingo@1527: }
ingo@2521: else if (type.equals("intoptions") && uiProvider.equals("parameter-matrix")) {
ingo@2532: list.add(DataFactory.createIntegerOptionsData(d, name, label));
ingo@2532: }
ingo@2532: else if (type.equals("options")) {
ingo@2532: list.add(DataFactory.createStringOptionsData(d, name, label));
ingo@2521: }
ingo@1527: else if (type.equals("intoptions")) {
ingo@1527: NodeList choices = ClientProtocolUtils.getItemNodes(d);
ingo@1527: DataItem[] opts = extractCurrentDataItems(choices);
ingo@1527:
ingo@1527: list.add(new IntegerOptionsData(name, label, opts));
ingo@1527: }
ingo@1595: else if (type.equals("doublearray")) {
ingo@1595: list.add(new DoubleArrayData(name, label, null));
ingo@1595: }
felix@1592: else {
felix@1592: logger.warn("Unrecognized Dynamic data type.");
felix@1592: NodeList choices = ClientProtocolUtils.getItemNodes(d);
felix@1592: DataItem[] dataItems = extractCurrentDataItems(choices);
felix@1592: DataItem def = extractDefaultDataItem(d);
felix@1592:
felix@1593: String min = ClientProtocolUtils.getMinNode(d);
felix@1593: String max = ClientProtocolUtils.getMaxNode(d);
felix@1593: if (min != null && max != null) {
felix@1593: list.add(new DoubleRangeData(
felix@1593: name, label,
felix@1593: Double.valueOf(min), Double.valueOf(max),
felix@1593: Double.valueOf(min), Double.valueOf(max)));
felix@1593: }
felix@1593:
felix@1592: list.add(new DefaultData(name, label, null, dataItems, def));
felix@1592: }
felix@1592:
ingo@51: }
ingo@51:
ingo@51: return list;
ingo@16: }
ingo@16:
ingo@16:
ingo@16: /**
ingo@515: * This method extracts the default value of a Data object.
ingo@515: *
ingo@515: * @param data The data object node.
ingo@515: *
ingo@515: * @return the default DataItem.
ingo@515: */
ingo@515: protected static DataItem extractDefaultDataItem(Node data) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractDefaultDataItem");
ingo@515:
ingo@515: String value = XMLUtils.xpathString(
ingo@515: data, "@art:defaultValue", ArtifactNamespaceContext.INSTANCE);
ingo@515:
ingo@515: String label = XMLUtils.xpathString(
ingo@515: data, "@art:defaultLabel", ArtifactNamespaceContext.INSTANCE);
ingo@515:
ingo@515: if (value != null && label != null) {
ingo@515: return new DefaultDataItem(label, null, value);
ingo@515: }
ingo@515:
ingo@515: return null;
ingo@515: }
ingo@515:
ingo@515:
ingo@515: /**
ingo@16: * This method extract the {@link DataItem}s of the DESCRIBE document.
ingo@16: *
ingo@16: * @param items The items in the DESCRIBE document.
ingo@16: *
ingo@16: * @return the {@link DataItem}s.
ingo@16: */
ingo@16: protected static DataItem[] extractCurrentDataItems(NodeList items) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractCurrentDataItems");
ingo@16:
ingo@16: if (items == null || items.getLength() == 0) {
ingo@1367: logger.debug("No data items found.");
ingo@16: return null;
ingo@16: }
ingo@16:
ingo@16: int count = items.getLength();
ingo@16:
ingo@16: List dataItems = new ArrayList(count);
ingo@16:
ingo@16: for (int i = 0; i < count; i++) {
ingo@16: Node item = items.item(i);
ingo@16: String label = ClientProtocolUtils.getLabel(item);
ingo@16: String value = ClientProtocolUtils.getValue(item);
ingo@16:
ingo@565: double[] mmQ = extractMinMaxQValues(item);
ingo@565: double[] mmW = extractMinMaxWValues(item);
ingo@565:
ingo@565: if (mmQ != null || mmW != null) {
ingo@565: dataItems.add(new WQDataItem(label, null, value, mmQ, mmW));
ingo@565: }
ingo@565: else {
ingo@565: dataItems.add(new DefaultDataItem(label, null, value));
ingo@565: }
ingo@16: }
ingo@16:
sascha@3379: return dataItems.toArray(new DataItem[count]);
ingo@16: }
ingo@22:
ingo@22:
ingo@565: protected static double[] extractMinMaxQValues(Node item) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractMinMaxQValues");
ingo@565:
ingo@565: if (item == null) {
ingo@1367: logger.debug("This node is empty - no min/max Q values.");
ingo@565: return null;
ingo@565: }
ingo@565:
ingo@565: Node node = (Node) XMLUtils.xpath(
ingo@565: item,
ingo@565: "art:range[@art:type='Q']",
ingo@565: XPathConstants.NODE,
ingo@565: ArtifactNamespaceContext.INSTANCE);
ingo@565:
ingo@565: if (node == null) {
ingo@1367: logger.debug("No min/max Q values found.");
ingo@565: return null;
ingo@565: }
ingo@565:
ingo@565: return extractMinMaxValues(node);
ingo@565: }
ingo@565:
ingo@565:
ingo@565: protected static double[] extractMinMaxWValues(Node item) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractMinMaxWValues");
ingo@565:
ingo@565: if (item == null) {
ingo@1367: logger.debug("This node is empty - no min/max W values.");
ingo@565: return null;
ingo@565: }
ingo@565:
ingo@565: Node node = (Node) XMLUtils.xpath(
ingo@565: item,
ingo@565: "art:range[@art:type='W']",
ingo@565: XPathConstants.NODE,
ingo@565: ArtifactNamespaceContext.INSTANCE);
ingo@565:
ingo@565: if (node == null) {
ingo@1367: logger.debug("No min/max W values found.");
ingo@565: return null;
ingo@565: }
ingo@565:
ingo@565: return extractMinMaxValues(node);
ingo@565: }
ingo@565:
ingo@565:
ingo@565: protected static double[] extractMinMaxValues(Node node) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractMinMaxValues");
ingo@565:
ingo@565: String minStr = XMLUtils.xpathString(
ingo@565: node, "art:min/text()", ArtifactNamespaceContext.INSTANCE);
ingo@565:
ingo@565: String maxStr = XMLUtils.xpathString(
ingo@565: node, "art:max/text()", ArtifactNamespaceContext.INSTANCE);
ingo@565:
ingo@565: if (maxStr == null || minStr == null) {
ingo@1367: logger.debug("No min/max values found.");
ingo@565: return null;
ingo@565: }
ingo@565:
ingo@565: try {
ingo@565: double min = Double.valueOf(minStr);
ingo@565: double max = Double.valueOf(maxStr);
ingo@565:
ingo@565: return new double[] { min, max };
ingo@565: }
ingo@565: catch (NumberFormatException nfe) {
ingo@1367: logger.debug("Error while parsing min/max values.");
ingo@565: }
ingo@565:
ingo@565: return null;
ingo@565: }
ingo@565:
ingo@565:
ingo@22: /**
ingo@34: * This method extracts the data objects from the data node of the static ui
ingo@34: * part of the DESCRIBE document.
ingo@34: *
ingo@34: * @param staticNode The static ui node of the DESCRIBE.
ingo@34: *
ingo@51: * @return the DataList objects.
ingo@34: */
ingo@51: protected static DataList[] extractOldData(Node staticNode) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractOldData()");
ingo@34:
ingo@51: NodeList stateNodes = (NodeList) XMLUtils.xpath(
ingo@34: staticNode,
ingo@51: XPATH_STATIC_STATE_NODE,
ingo@34: XPathConstants.NODESET,
ingo@34: ArtifactNamespaceContext.INSTANCE);
ingo@34:
ingo@51: if (stateNodes == null || stateNodes.getLength() == 0) {
ingo@1367: logger.debug("No old items found.");
ingo@34: return null;
ingo@34: }
ingo@34:
ingo@51: int count = stateNodes.getLength();
ingo@51: DataList[] data = new DataList[count];
ingo@34:
ingo@34: for (int i = 0; i < count; i++) {
ingo@51: Node tmp = stateNodes.item(i);
ingo@34:
ingo@34: String name = XMLUtils.xpathString(
ingo@34: tmp, "@art:name", ArtifactNamespaceContext.INSTANCE);
ingo@52: String uiprovider = XMLUtils.xpathString(
ingo@52: tmp, "@art:uiprovider", ArtifactNamespaceContext.INSTANCE);
ingo@52: String label = XMLUtils.xpathString(
ingo@52: tmp, "@art:label", ArtifactNamespaceContext.INSTANCE);
ingo@2500: String help = XMLUtils.xpathString(
ingo@2500: tmp, "@art:helpText", ArtifactNamespaceContext.INSTANCE);
ingo@34:
ingo@51: NodeList dataNodes = (NodeList) XMLUtils.xpath(
ingo@51: tmp,
ingo@51: XPATH_STATIC_DATA_NODE,
ingo@51: XPathConstants.NODESET,
ingo@51: ArtifactNamespaceContext.INSTANCE);
ingo@34:
ingo@51: if (dataNodes == null || dataNodes.getLength() == 0) {
ingo@51: continue;
ingo@51: }
ingo@51:
ingo@51: int size = dataNodes.getLength();
ingo@2500: DataList list = new DataList(name, size, uiprovider, label, help);
ingo@51:
ingo@51: for (int j = 0; j < size; j++) {
ingo@51: Node dataNode = dataNodes.item(j);
ingo@51:
ingo@1571: list.add(DataFactory.createDataFromElement((Element) dataNode));
ingo@51:
ingo@51: data[i] = list;
ingo@51: }
ingo@34: }
ingo@34:
ingo@34: return data;
ingo@34: }
ingo@34:
ingo@34:
ingo@34: /**
ingo@22: * This method extracts the UIProvider specified by the data node.
ingo@22: *
ingo@22: * @param data The data node.
ingo@22: *
ingo@22: * @return the UIProvider that is specified in the data node.
ingo@22: */
ingo@51: protected static String extractUIProvider(Node ui) {
ingo@22: return (String) XMLUtils.xpath(
ingo@51: ui,
ingo@22: XPATH_UIPROVIDER,
ingo@22: XPathConstants.STRING,
ingo@22: ArtifactNamespaceContext.INSTANCE);
ingo@22: }
ingo@32:
ingo@32:
ingo@32: /**
ingo@2500: * This method extracts the help text specified by the data node.
ingo@2500: *
ingo@2500: * @param ui The data node.
ingo@2500: *
ingo@2500: * @return the help text.
ingo@2500: */
ingo@2500: protected static String extractHelpText(Node ui) {
ingo@2500: return (String) XMLUtils.xpath(
ingo@2500: ui,
ingo@2500: XPATH_HELP_TEXT,
ingo@2500: XPathConstants.STRING,
ingo@2500: ArtifactNamespaceContext.INSTANCE);
ingo@2500: }
ingo@2500:
ingo@2500:
ingo@2500: /**
ingo@32: * This method extracts the reachable states of the current artifact.
ingo@32: *
ingo@32: * @param reachable The reachable states node.
ingo@32: *
ingo@32: * @return an array with identifiers of reachable states.
ingo@32: */
ingo@32: protected static String[] extractReachableStates(Node reachable) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractReachableStates()");
ingo@32:
ingo@32: NodeList list = (NodeList) XMLUtils.xpath(
ingo@32: reachable,
ingo@32: XPATH_REACHABLE_STATE,
ingo@32: XPathConstants.NODESET,
ingo@32: ArtifactNamespaceContext.INSTANCE);
ingo@32:
ingo@32: if (list == null || list.getLength() == 0) {
ingo@32: return null;
ingo@32: }
ingo@32:
ingo@32: int count = list.getLength();
ingo@32:
ingo@32: String[] states = new String[count];
ingo@32:
ingo@32: for (int i = 0; i < count; i++) {
ingo@32: Node state = list.item(i);
ingo@32:
ingo@32: String name = XMLUtils.xpathString(
ingo@32: state, "@art:name", ArtifactNamespaceContext.INSTANCE);
ingo@32:
ingo@32: states[i] = name;
ingo@32: }
ingo@32:
ingo@32: return states;
ingo@32: }
ingo@65:
ingo@65:
ingo@65: /**
ingo@65: * This method extract available output modes of the the current artifact.
ingo@65: *
ingo@65: * @param outputs A list of nodes that contain information about output
ingo@65: * modes.
ingo@65: *
ingo@65: * @return an array of Output modes.
ingo@65: */
ingo@65: protected static OutputMode[] extractOutputModes(NodeList outputs) {
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractOutputModes");
ingo@65:
ingo@65: if (outputs == null || outputs.getLength() == 0) {
ingo@65: return null;
ingo@65: }
ingo@65:
ingo@65: int size = outputs.getLength();
ingo@65:
ingo@65: List outs = new ArrayList(size);
ingo@65:
ingo@65: for (int i = 0; i < size; i++) {
ingo@65: Node out = outputs.item(i);
ingo@65:
ingo@65: String name = XMLUtils.xpathString(
ingo@65: out, "@art:name", ArtifactNamespaceContext.INSTANCE);
ingo@65: String desc = XMLUtils.xpathString(
ingo@65: out, "@art:description", ArtifactNamespaceContext.INSTANCE);
ingo@65: String mimeType = XMLUtils.xpathString(
ingo@65: out, "@art:mime-type", ArtifactNamespaceContext.INSTANCE);
ingo@65:
ingo@65: if (name != null) {
ingo@65: outs.add(new DefaultOutputMode(name, desc, mimeType));
ingo@65: }
ingo@65: else {
ingo@1367: logger.debug("Found an invalid output mode.");
ingo@65: }
ingo@65: }
ingo@65:
ingo@65: return (OutputMode[]) outs.toArray(new OutputMode[size]);
ingo@65: }
ingo@803:
ingo@803:
ingo@807: protected static Recommendation[] extractRecommendedArtifacts(Document doc){
ingo@1367: logger.debug("ArtifactDescriptionFactory.extractRecommendedArtifacts.");
ingo@803:
ingo@803: NodeList list = (NodeList) XMLUtils.xpath(
ingo@803: doc,
ingo@803: XPATH_RECOMMENDED_ARTIFACTS,
ingo@803: XPathConstants.NODESET,
ingo@803: ArtifactNamespaceContext.INSTANCE);
ingo@803:
ingo@803: int num = list != null ? list.getLength() : 0;
ingo@803:
ingo@807: Recommendation[] rec = new Recommendation[num];
ingo@803:
ingo@803: for (int i = 0; i < num; i++) {
ingo@807: Element e = (Element) list.item(i);
ingo@807: String factory = e.getAttribute("factory");
sascha@836: String index = e.getAttribute("ids");
ingo@803:
ingo@807: if (factory != null && factory.length() > 0) {
sascha@839: rec[i] = new Recommendation(factory, index);
ingo@803: }
ingo@803: }
ingo@803:
ingo@807: return rec;
ingo@803: }
ingo@16: }
ingo@16: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :