view gnv-artifacts/src/main/java/de/intevation/gnv/artifacts/services/MetaDataService.java @ 800:db5b04ecb426

ISSUE215: Improved ODV-Export. now all Columns which have identical values but different Parameters will be merged to one row. gnv-artifacts/trunk@882 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Tim Englich <tim.englich@intevation.de>
date Tue, 06 Apr 2010 13:07:11 +0000
parents 9b9bf42b7928
children feae2f9d6c6f
line wrap: on
line source
package de.intevation.gnv.artifacts.services;

import com.vividsolutions.jts.geom.Geometry;

import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

import de.intevation.artifactdatabase.DefaultService;
import de.intevation.artifactdatabase.XMLUtils;

import de.intevation.artifacts.ArtifactNamespaceContext;
import de.intevation.artifacts.CallMeta;
import de.intevation.artifacts.ServiceFactory;

import de.intevation.gnv.artifacts.services.requestobjects.DefaultFIS;
import de.intevation.gnv.artifacts.services.requestobjects.DefaultLayer;
import de.intevation.gnv.artifacts.services.requestobjects.DefaultMapService;
import de.intevation.gnv.artifacts.services.requestobjects.DefaultParameter;
import de.intevation.gnv.artifacts.services.requestobjects.FIS;
import de.intevation.gnv.artifacts.services.requestobjects.Layer;
import de.intevation.gnv.artifacts.services.requestobjects.MapService;
import de.intevation.gnv.artifacts.services.requestobjects.Parameter;

import de.intevation.gnv.geobackend.base.Result;

import de.intevation.gnv.geobackend.base.query.QueryExecutor;
import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;

import de.intevation.gnv.geobackend.base.query.exception.QueryException;

import de.intevation.gnv.utils.ArtifactXMLUtilities;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.xml.xpath.XPathConstants;

import org.apache.log4j.Logger;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a>
 *
 */
public class MetaDataService extends DefaultService {

    /**
     * the logger, used to log exceptions and additionally information
     */
    private static Logger log = Logger.getLogger(MetaDataService.class);


    private final static String FIS_REGION_QUERY_ID =
                                      "mapviewer_interface_fis_region";
    private final static String MAPSERVICES_HAS_FIS_QUERY_ID =
                                      "mapviewer_interface_mapservices_has_fis";
    private final static String MAPSERVICES_HAS_PARAMETER_QUERY_ID =
                                "mapviewer_interface_mapservices_has_parameter";

    private final static String MAPSERVICES_HAS_PARAMETER_USING_LAYER_QUERY_ID =
        "mapviewer_interface_mapservices_has_parameter_using_layer";

    private static String ATTRIBUTE_ID = "id";
    private static String ATTRIBUTE_NAME = "name";
    private static String ATTRIBUTE_TYPE = "type";
    private static String ATTRIBUTE_URL = "url";
    private static String ATTRIBUTE_GROUPLAYER = "isgrouplayer";
    private static String ATTRIBUTE_PARENTID = "parentid";
    private static String ATTRIBUTE_SRS = "srs";


    private static String XPATH_LOACTION_NODE = "art:GetMetaData/art:location";
    private static String XPATH_MAPSERVICES_NODESET = "art:GetMetaData/" +
                                                      "art:mapservices/" +
                                                      "art:mapservice";
    private static String XPATH_LAYER_NODESET = "art:layer";

    /**
     * The UID of this Class.
     */
    private static final long serialVersionUID = -8446483887497236372L;

    /**
     * Constructor
     */
    public MetaDataService() {
        super();
    }

    /**
     * @param data
     * @param globalContext 
     * @param callMeta
     * @return
     * @see de.intevation.artifactdatabase.DefaultService#process(org.w3c.dom.Document, java.lang.Object, de.intevation.artifacts.CallMeta)
     */
    @Override
    @SuppressWarnings({"static-access", "static-access"})
    public Document process(Document data, Object globalContext,
                            CallMeta callMeta) {
        log.debug("MetaDataService.process");
        Document document = null;
        try {
            Geometry g = this.parseGeometry(data);
            Collection<MapService> mapServices = this.parseMapServices(data);
            Collection<FIS> resultFIS = this.unionFIS(this.getFIS(g),
                                                      this.getFIS(mapServices));
            document = XMLUtils.newDocument();
            this.writeFIS2Document(document, resultFIS);
            log.debug(ArtifactXMLUtilities.writeDocument2String(document));
        } catch (MetaDataServiceException e) {
            log.error(e,e);
            document = ArtifactXMLUtilities
                          .createExceptionReport(e.getMessage(), document);
        }
        return document;
    }

    private Geometry parseGeometry(Document data)
                                   throws MetaDataServiceException{
        log.debug("MetaDataService.parseGeometry");

        Element locationNode = (Element) XMLUtils.xpath(
                data,
                XPATH_LOACTION_NODE,
                XPathConstants.NODE,
                ArtifactNamespaceContext.INSTANCE
            );
        Geometry returnValue = null;
        if (locationNode != null) {
            String srs = locationNode.getAttribute(ATTRIBUTE_SRS);
            // TODO: use SRS to transform the Geometry to target-System.
            String geometryValue = locationNode.getTextContent();
            if (geometryValue != null){
                try {
                    returnValue = new WKTReader().read(geometryValue);
                } catch (ParseException e) {
                    log.error(e,e);
                    throw new MetaDataServiceException("The given Geometry" +
                                                       "String is not a " +
                                                       "valid WKT.");
                }
            }
        }
        return returnValue;
    }

    private Collection<MapService> parseMapServices(Document data){
        log.debug("MetaDataService.parseMapServices");

        NodeList mapServices = (NodeList) XMLUtils.xpath(data,
                                             XPATH_MAPSERVICES_NODESET,
                                             XPathConstants.NODESET,
                                             ArtifactNamespaceContext.INSTANCE);
        Collection<MapService> returnValue = null;
        if (mapServices != null){
            returnValue = new ArrayList<MapService>(mapServices.getLength());
            for (int i = 0; i < mapServices.getLength(); i++){
                Element mapServiceNode = (Element)mapServices.item(i);
                String id = mapServiceNode.getAttribute(ATTRIBUTE_ID);
                String type = mapServiceNode.getAttribute(ATTRIBUTE_TYPE);
                String url = mapServiceNode.getAttribute(ATTRIBUTE_URL);
                Collection<Layer> layer = null;

                NodeList layerNodes = (NodeList) XMLUtils.xpath(mapServiceNode,
                                            XPATH_LAYER_NODESET,
                                            XPathConstants.NODESET,
                                            ArtifactNamespaceContext.INSTANCE);
                if (layerNodes != null){
                    layer = new ArrayList<Layer>(layerNodes.getLength());
                    for (int j = 0; j < layerNodes.getLength(); j++){
                        Element layerNode = (Element)layerNodes.item(j);
                        String layerId = layerNode.getAttribute(ATTRIBUTE_ID);
                        String layerName = layerNode.getAttribute(ATTRIBUTE_NAME);
                        boolean isGroupLayer =
                                      Boolean.parseBoolean(layerNode
                                                      .getAttribute(
                                                         ATTRIBUTE_GROUPLAYER));
                        String parentId = layerNode
                                              .getAttribute(ATTRIBUTE_PARENTID);

                        layer.add(new DefaultLayer(layerId,
                                                   layerName,
                                                   isGroupLayer,
                                                   parentId));
                    }
                }
                MapService mapService = new DefaultMapService(id, layer,
                                                              type, url);
                returnValue.add(mapService);

            }
        }
        return returnValue;
    }

    private Collection<FIS> unionFIS(Collection<FIS> fromGeometry,
                                        Collection<FIS> fromMapservices){
        log.debug("MetaDataService.unionFIS");
        Collection<FIS> returnValue = null;
        if (fromGeometry == null || fromGeometry.isEmpty()){
            returnValue = fromMapservices;
        }else if (fromMapservices == null || fromMapservices.isEmpty()){
            returnValue = fromGeometry;
        }else{

            returnValue = new ArrayList<FIS>();
            Iterator<FIS> it = fromMapservices.iterator();
            while (it.hasNext()){
                FIS fis = it.next();
                if (fromGeometry.contains(fis)){
                    returnValue.add(fis);
                }
            }
        }
        return returnValue;
    }

    /**
     * Puts the retrieved FIS into the given XML-Document.
     * @param document the Document where the FIS should be put in.
     * @param fis the retrieved FIS which should be written into
     *            the XML-Document.
     */
    private void writeFIS2Document(Document document, Collection<FIS> fis){

        if (fis != null){
            Iterator<FIS> it = fis.iterator();
            XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
                    document,
                    ArtifactNamespaceContext.NAMESPACE_URI,
                    ArtifactNamespaceContext.NAMESPACE_PREFIX
                );
            Node rootNode = creator.create("result");
            document.appendChild(rootNode);

            Node factoriesNode = creator.create("factories");
            rootNode.appendChild(factoriesNode);
            while (it.hasNext()){
                FIS tmpFIS = it.next();
                Element fisNode = creator.create("factory");
                fisNode.setAttribute("art:name",tmpFIS.getID());

                Collection<Parameter> parameter = tmpFIS.getParameter();

                if(parameter != null){
                    Iterator<Parameter> pit = parameter.iterator();
                    while (pit.hasNext()){
                        Parameter p = pit.next();
                        Element parameterNode = creator.create("parameter");
                        parameterNode.setAttribute(ATTRIBUTE_ID, p.getID());
                        parameterNode.setAttribute(ATTRIBUTE_NAME, p.getName());
                        fisNode.appendChild(parameterNode);
                    }
                }
                factoriesNode.appendChild(fisNode);
            }
        }
    }

    /**
     * Returns all FIS which Areas is intersected by this given Geometry
     * @param g the Geometry which should be used to determine the FIS.
     * @return all FIS which Areas is intersected by this given Geometry
     * @throws MetaDataServiceException
     */
    protected Collection<FIS> getFIS(Geometry g)
                                        throws MetaDataServiceException{
        log.debug("MetaDataService.getFIS ==> Geometry");
        Collection<FIS> resultValue = null;
        if (g != null){
            try {
                QueryExecutor queryExecutor = QueryExecutorFactory
                                               .getInstance()
                                               .getQueryExecutor();
                Collection<Result> result = queryExecutor.executeQuery(FIS_REGION_QUERY_ID,
                                                    new String[]{g.toString()});

                if (result != null){
                    resultValue = new ArrayList<FIS>(result.size());
                    Iterator<Result> it = result.iterator();
                    while (it.hasNext()){
                        Result value = it.next();
                        String fis_id = value.getString(0).trim();
                        resultValue.add(new DefaultFIS(fis_id));
                    }
                }
            } catch (QueryException e) {
                log.error(e,e);
                throw new MetaDataServiceException("Cannot Query FIS from DB.");
            }
        }
        return resultValue;
    }

    /**
     * Returns all FIS which were represented by the given Mapservices
     * @param mapServices the Mapservices which should determine the FIS.
     * @return all FIS which where represented my the given Mapservices.
     * @throws MetaDataServiceException
     */
    protected Collection<FIS> getFIS(Collection<MapService> mapServices)
                                              throws MetaDataServiceException{
        log.debug("MetaDataService.getFIS ==> MapServices");
        Collection<FIS> resultValue = null;
        if (mapServices != null && !mapServices.isEmpty()){
            try {

                String mapServiceNames = "";
                Iterator<MapService> mit = mapServices.iterator();
                while(mit.hasNext()){
                    if (mapServiceNames.length() > 0){
                        mapServiceNames += " , ";
                    }
                    mapServiceNames += "'"+mit.next().getID()+"'";
                }

                QueryExecutor queryExecutor = QueryExecutorFactory
                                               .getInstance()
                                               .getQueryExecutor();
                Collection<Result> result = queryExecutor.executeQuery(
                                                  MAPSERVICES_HAS_FIS_QUERY_ID,
                                                  new String[]{mapServiceNames});
                if (result != null){
                    resultValue = new ArrayList<FIS>(result.size());
                    Iterator<Result> it = result.iterator();
                    while (it.hasNext()){
                        Result value = it.next();
                        String fisId = value.getString(0).trim();
                        String mapServiceID = value.getString(1).trim();

                        // FIRST LOOK IF ONE MAPSERVICE REPRESENTS ONLY ONE PARAM
                        Collection<Result> result2 = queryExecutor.executeQuery(
                                MAPSERVICES_HAS_PARAMETER_QUERY_ID,
                                new String[]{"'"+mapServiceID+"'"});
                        Collection<Parameter> parameter = null;
                        if (result2 != null && result2.size() == 1){
                            Iterator<Result> it2 = result2.iterator();
                            parameter = new ArrayList<Parameter>(1);
                            while (it2.hasNext()){
                                Result parameterValue = it2.next();
                                String parameterID = parameterValue.getString(0)
                                                                   .trim();
                                parameter.add(new DefaultParameter(parameterID,
                                                                   parameterID));
                            }
                        }else{
                            // IF FALSE LOOK IF THE GIVEN LAYERs TO AN MAPSERVICE
                            // REPRESENTS DIFFERENT PARAMS
                            MapService service = this.getMapService(mapServices,
                                                                    mapServiceID);
                            Collection<Layer> layer = service.getLayer();
                            if (layer != null && !layer.isEmpty()){
                                String layerQueryString =
                                    this.createLayerQueryString(layer);
                                Collection<Result> parameterResult =
                                    queryExecutor.executeQuery(
                                        MAPSERVICES_HAS_PARAMETER_USING_LAYER_QUERY_ID,
                                        new String[]{"'"+mapServiceID+"'",
                                                     layerQueryString});
                                if (parameterResult != null &&
                                    !parameterResult.isEmpty()){
                                    Iterator<Result> it2 = parameterResult.iterator();
                                    parameter = new ArrayList<Parameter>(parameterResult.size());
                                    while (it2.hasNext()){
                                        Result parameterValue = it2.next();
                                        String parameterID = parameterValue.getString(0)
                                                                           .trim();
                                        parameter.add(new DefaultParameter(parameterID,
                                                                           parameterID));
                                    }
                                }
                            }

                        }

                        FIS fis = this.getFIS(resultValue, fisId);
                        if (fis != null){
                            if (parameter != null){
                                fis.addParameter(parameter);
                            }
                        }else{
                            resultValue.add(new DefaultFIS(fisId, parameter));
                        }
                    }
                }
            } catch (QueryException e) {
                log.error(e,e);
                throw new MetaDataServiceException("Cannot Query FIS from DB.");
            }

        }
        return resultValue;
    }


    private FIS getFIS (Collection<FIS> fis, String fisId){

        Iterator<FIS> it = fis.iterator();
        while(it.hasNext()){
            FIS tmpFIS = it.next();
            if (tmpFIS.getID().equals(fisId)){
                return tmpFIS;
            }
        }
        return null;
    }

    private MapService getMapService(Collection<MapService> mapServices,
                                     String mapServiceID){
        log.debug("MetaDataService.getMapService");
        Iterator<MapService> it = mapServices.iterator();
        while (it.hasNext()){
            MapService service = it.next();
            if (service.getID().equals(mapServiceID)){
                return service;
            }
        }
        return null;
    }

    @SuppressWarnings("empty-statement")
    private String createLayerQueryString(Collection<Layer> layer){
        log.debug("MetaDataService.createLayerQueryString");
        StringBuffer sb = new StringBuffer();;
        Iterator<Layer> it = layer.iterator();
        synchronized (sb) {
            while (it.hasNext()){
                Layer l = it.next();
                if (!l.isGroupLayer()){
                    sb.append(l.getID());
                    if (it.hasNext()){
                        sb.append(" , ");
                    }
                }

            }
        }
        String returnValue = sb.toString();
        if (returnValue.endsWith(" , ")){
            returnValue = returnValue.substring(0,returnValue
                                                  .lastIndexOf(","))
                                                  .trim();
        }
        return returnValue;
    }

    /**
     * @param factory 
     * @param globalContext
     * @see de.intevation.artifactdatabase.DefaultService#setup(de.intevation.artifacts.ServiceFactory, java.lang.Object)
     */
    @Override
    public void setup(ServiceFactory factory, Object globalContext) {
        log.debug("MetaDataService.setup");
        super.setup(factory, globalContext);
        // TODO: Perhaps it is necessary to init the QueryIds here.
    }

}

http://dive4elements.wald.intevation.org