tim@597: /**
tim@597:  *
tim@597:  */
tim@597: package de.intevation.gnv.artifacts.services;
tim@597: 
tim@597: import java.util.ArrayList;
tim@597: import java.util.Collection;
tim@597: import java.util.Iterator;
tim@597: 
tim@597: import javax.xml.xpath.XPathConstants;
tim@597: 
tim@597: import org.apache.log4j.Logger;
tim@597: import org.w3c.dom.Document;
tim@597: import org.w3c.dom.Element;
tim@597: import org.w3c.dom.Node;
tim@597: import org.w3c.dom.NodeList;
tim@597: 
tim@597: import com.vividsolutions.jts.geom.Geometry;
tim@597: import com.vividsolutions.jts.io.ParseException;
tim@597: import com.vividsolutions.jts.io.WKTReader;
tim@597: 
tim@597: import de.intevation.artifactdatabase.DefaultService;
tim@597: import de.intevation.artifactdatabase.XMLUtils;
tim@597: import de.intevation.artifacts.ArtifactNamespaceContext;
tim@597: import de.intevation.artifacts.CallMeta;
tim@597: import de.intevation.artifacts.ServiceFactory;
tim@597: import de.intevation.gnv.artifacts.services.requestobjects.DefaultFIS;
tim@597: import de.intevation.gnv.artifacts.services.requestobjects.DefaultLayer;
tim@597: import de.intevation.gnv.artifacts.services.requestobjects.DefaultMapService;
tim@604: import de.intevation.gnv.artifacts.services.requestobjects.DefaultParameter;
tim@597: import de.intevation.gnv.artifacts.services.requestobjects.FIS;
tim@597: import de.intevation.gnv.artifacts.services.requestobjects.Layer;
tim@597: import de.intevation.gnv.artifacts.services.requestobjects.MapService;
tim@597: import de.intevation.gnv.artifacts.services.requestobjects.Parameter;
tim@597: import de.intevation.gnv.geobackend.base.Result;
tim@597: import de.intevation.gnv.geobackend.base.query.QueryExecutor;
tim@597: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
tim@597: import de.intevation.gnv.geobackend.base.query.exception.QueryException;
tim@597: import de.intevation.gnv.utils.ArtifactXMLUtilities;
tim@597: 
tim@597: /**
tim@597:  * @author Tim Englich <tim.englich@intevation.de>
tim@597:  *
tim@597:  */
tim@597: public class MetaDataService extends DefaultService {
tim@597: 
tim@597:     /**
tim@597:      * the logger, used to log exceptions and additionally information
tim@597:      */
tim@597:     private static Logger log = Logger.getLogger(MetaDataService.class);
tim@597: 
tim@597:     
tim@604:     private final static String FIS_REGION_QUERY_ID = 
tim@604:                                       "mapviewer_interface_fis_region";
tim@597:     private final static String MAPSERVICES_HAS_FIS_QUERY_ID = 
tim@604:                                       "mapviewer_interface_mapservices_has_fis";
tim@604:     private final static String MAPSERVICES_HAS_PARAMETER_QUERY_ID = 
tim@604:                                 "mapviewer_interface_mapservices_has_parameter";
tim@604:     
tim@604:     private final static String MAPSERVICES_HAS_PARAMETER_USING_LAYER_QUERY_ID = 
tim@604:         "mapviewer_interface_mapservices_has_parameter_using_layer";
tim@597:     
tim@597:     private static String ATTRIBUTE_ID = "id";
tim@597:     private static String ATTRIBUTE_NAME = "name";
tim@597:     private static String ATTRIBUTE_TYPE = "type";
tim@597:     private static String ATTRIBUTE_URL = "url";
tim@597:     private static String ATTRIBUTE_GROUPLAYER = "isgrouplayer";
tim@597:     private static String ATTRIBUTE_PARENTID = "parentid";
tim@597:     private static String ATTRIBUTE_SRS = "srs";
tim@597:     
tim@597:     
tim@597:     private static String XPATH_LOACTION_NODE = "art:GetMetaData/art:location";
tim@597:     private static String XPATH_MAPSERVICES_NODESET = "art:GetMetaData/" +
tim@597:                                                       "art:mapservices/" +
tim@597:                                                       "art:mapservice";
tim@597:     private static String XPATH_LAYER_NODESET = "art:layer";
tim@597:     
tim@597:     /**
tim@597:      * The UID of this Class.
tim@597:      */
tim@597:     private static final long serialVersionUID = -8446483887497236372L;
tim@597: 
tim@597:     /**
tim@597:      * Constructor
tim@597:      */
tim@597:     public MetaDataService() {
tim@597:         super();
tim@597:     }
tim@597: 
tim@597:     /**
tim@597:      * @see de.intevation.artifactdatabase.DefaultService#process(org.w3c.dom.Document, java.lang.Object, de.intevation.artifacts.CallMeta)
tim@597:      */
tim@597:     @Override
tim@597:     public Document process(Document data, Object globalContext,
tim@597:                             CallMeta callMeta) {
tim@597:         log.debug("MetaDataService.process");
tim@597:         Document document = null;
tim@597:         try {
tim@597:             Geometry g = this.parseGeometry(data);
tim@597:             Collection<MapService> mapServices = this.parseMapServices(data);
tim@597:             Collection<FIS> resultFIS = this.unionFIS(this.getFIS(g), 
tim@597:                                                       this.getFIS(mapServices));
tim@597:             document = XMLUtils.newDocument();
tim@597:             this.writeFIS2Document(document, resultFIS);
tim@604:             log.debug(new ArtifactXMLUtilities().writeDocument2String(document));
tim@597:         } catch (MetaDataServiceException e) {
tim@597:             log.error(e,e);
tim@597:             document = new ArtifactXMLUtilities()
tim@597:                           .createExceptionReport(e.getMessage(), document);
tim@597:         }
tim@597:         return document;
tim@597:     }
tim@597:     
tim@597:     private Geometry parseGeometry(Document data) 
tim@597:                                    throws MetaDataServiceException{
tim@597:         log.debug("MetaDataService.parseGeometry");
tim@597:         
tim@597:         Element locationNode = (Element) XMLUtils.xpath(
tim@597:                 data,
tim@597:                 XPATH_LOACTION_NODE,
tim@597:                 XPathConstants.NODE,
tim@597:                 ArtifactNamespaceContext.INSTANCE
tim@597:             );
tim@597:         Geometry returnValue = null;
tim@597:         if (locationNode != null) {
tim@597:             String srs = locationNode.getAttribute(ATTRIBUTE_SRS);
tim@597:             // TODO: use SRS to transform the Geometry to target-System.
tim@597:             String geometryValue = locationNode.getTextContent();
tim@597:             if (geometryValue != null){
tim@597:                 try {
tim@597:                     returnValue = new WKTReader().read(geometryValue);
tim@597:                 } catch (ParseException e) {
tim@597:                     log.error(e,e);
tim@597:                     throw new MetaDataServiceException("The given Geometry" +
tim@597:                                                        "String is not a " +
tim@597:                                                        "valid WKT.");
tim@597:                 }
tim@597:             }
tim@597:         }
tim@597:         return returnValue;
tim@597:     }
tim@597:     
tim@597:     private Collection<MapService> parseMapServices(Document data){
tim@597:         log.debug("MetaDataService.parseMapServices");
tim@597:         
tim@597:         NodeList mapServices = (NodeList) XMLUtils.xpath(data, 
tim@597:                                              XPATH_MAPSERVICES_NODESET,
tim@597:                                              XPathConstants.NODESET,
tim@597:                                              ArtifactNamespaceContext.INSTANCE);
tim@597:         Collection<MapService> returnValue = null;
tim@597:         if (mapServices != null){
tim@597:             returnValue = new ArrayList<MapService>(mapServices.getLength());
tim@597:             for (int i = 0; i < mapServices.getLength(); i++){
tim@597:                 Element mapServiceNode = (Element)mapServices.item(i);
tim@597:                 String id = mapServiceNode.getAttribute(ATTRIBUTE_ID);
tim@597:                 String type = mapServiceNode.getAttribute(ATTRIBUTE_TYPE);
tim@597:                 String url = mapServiceNode.getAttribute(ATTRIBUTE_URL);
tim@597:                 Collection<Layer> layer = null;
tim@597:                 
tim@597:                 NodeList layerNodes = (NodeList) XMLUtils.xpath(mapServiceNode, 
tim@597:                                             XPATH_LAYER_NODESET,
tim@597:                                             XPathConstants.NODESET,
tim@597:                                             ArtifactNamespaceContext.INSTANCE);
tim@597:                 if (layerNodes != null){
tim@597:                     layer = new ArrayList<Layer>(layerNodes.getLength());
tim@597:                     for (int j = 0; j < layerNodes.getLength(); j++){
tim@597:                         Element layerNode = (Element)layerNodes.item(j);
tim@597:                         String layerId = layerNode.getAttribute(ATTRIBUTE_ID);
tim@597:                         String layerName = layerNode.getAttribute(ATTRIBUTE_NAME);
tim@597:                         boolean isGroupLayer = 
tim@597:                                       Boolean.parseBoolean(layerNode
tim@597:                                                       .getAttribute(
tim@597:                                                          ATTRIBUTE_GROUPLAYER));
tim@597:                         String parentId = layerNode
tim@597:                                               .getAttribute(ATTRIBUTE_PARENTID);
tim@597:                         
tim@597:                         layer.add(new DefaultLayer(layerId,
tim@597:                                                    layerName,
tim@597:                                                    isGroupLayer,
tim@597:                                                    parentId));
tim@597:                     }
tim@597:                 }
tim@597:                 MapService mapService = new DefaultMapService(id, layer, 
tim@597:                                                               type, url);
tim@597:                 returnValue.add(mapService);
tim@597:                 
tim@597:             }
tim@597:         }
tim@597:         return returnValue;
tim@597:     }
tim@597:     
tim@597:     private Collection<FIS> unionFIS(Collection<FIS> fromGeometry, 
tim@597:                                         Collection<FIS> fromMapservices){
tim@597:         log.debug("MetaDataService.unionFIS");
tim@597:         Collection<FIS> returnValue = null;
tim@597:         if (fromGeometry == null || fromGeometry.isEmpty()){
tim@597:             returnValue = fromMapservices;
tim@597:         }else if (fromMapservices == null || fromMapservices.isEmpty()){
tim@597:             returnValue = fromGeometry;
tim@597:         }else{
tim@597:             
tim@597:             returnValue = new ArrayList<FIS>();
tim@597:             Iterator<FIS> it = fromMapservices.iterator();
tim@597:             while (it.hasNext()){
tim@597:                 FIS fis = it.next();
tim@597:                 if (fromGeometry.contains(fis)){
tim@597:                     returnValue.add(fis);
tim@597:                 }
tim@597:             }
tim@597:         }
tim@597:         return returnValue;
tim@597:     }
tim@597:     
tim@597:     /**
tim@597:      * Puts the retrieved FIS into the given XML-Document.
tim@597:      * @param document the Document where the FIS should be put in.
tim@597:      * @param fis the retrieved FIS which should be written into 
tim@597:      *            the XML-Document.
tim@597:      */
tim@597:     private void writeFIS2Document(Document document, Collection<FIS> fis){
tim@597:         
tim@597:         if (fis != null){
tim@597:             Iterator<FIS> it = fis.iterator();
tim@597:             XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
tim@597:                     document,
tim@597:                     ArtifactNamespaceContext.NAMESPACE_URI,
tim@597:                     ArtifactNamespaceContext.NAMESPACE_PREFIX
tim@597:                 );
tim@597:             Node rootNode = creator.create("result");
tim@597:             document.appendChild(rootNode);
tim@597:             
tim@597:             Node factoriesNode = creator.create("factories");
tim@597:             rootNode.appendChild(factoriesNode);
tim@597:             while (it.hasNext()){
tim@597:                 FIS tmpFIS = it.next();
tim@597:                 Element fisNode = creator.create("factory");
tim@597:                 fisNode.setAttribute("art:name",tmpFIS.getID());
tim@597:                 
tim@597:                 Collection<Parameter> parameter = tmpFIS.getParameter();
tim@597:                 
tim@597:                 if(parameter != null){
tim@597:                     Iterator<Parameter> pit = parameter.iterator();
tim@597:                     while (pit.hasNext()){
tim@597:                         Parameter p = pit.next();
tim@597:                         Element parameterNode = creator.create("parameter");
tim@597:                         parameterNode.setAttribute(ATTRIBUTE_ID, p.getID());
tim@597:                         parameterNode.setAttribute(ATTRIBUTE_NAME, p.getName());
tim@597:                         fisNode.appendChild(parameterNode);
tim@597:                     }
tim@597:                 }
tim@597:                 factoriesNode.appendChild(fisNode);
tim@597:             }
tim@597:         }
tim@597:     }
tim@597:     
tim@597:     /**
tim@597:      * Returns all FIS which Areas is intersected by this given Geometry
tim@597:      * @param g the Geometry which should be used to determine the FIS.
tim@597:      * @return all FIS which Areas is intersected by this given Geometry
tim@597:      */
tim@597:     protected Collection<FIS> getFIS(Geometry g) 
tim@597:                                         throws MetaDataServiceException{
tim@597:         log.debug("MetaDataService.getFIS ==> Geometry");
tim@597:         Collection<FIS> resultValue = null;
tim@597:         if (g != null){
tim@597:             try {
tim@597:                 QueryExecutor queryExecutor = QueryExecutorFactory
tim@597:                                                .getInstance()
tim@597:                                                .getQueryExecutor();
tim@597:                 Collection<Result> result = queryExecutor.executeQuery(FIS_REGION_QUERY_ID,
tim@597:                                                     new String[]{g.toString()});
tim@597:                 
tim@597:                 if (result != null){
tim@597:                     resultValue = new ArrayList<FIS>(result.size());
tim@597:                     Iterator<Result> it = result.iterator();
tim@597:                     while (it.hasNext()){
tim@597:                         Result value = it.next();
tim@604:                         String fis_id = value.getString(0).trim();
tim@597:                         resultValue.add(new DefaultFIS(fis_id));
tim@597:                     }
tim@597:                 }
tim@597:             } catch (QueryException e) {
tim@597:                 log.error(e,e);
tim@597:                 throw new MetaDataServiceException("Cannot Query FIS from DB.");
tim@597:             }
tim@597:         }
tim@597:         return resultValue;
tim@597:     }
tim@597:     
tim@597:     /**
tim@597:      * Returns all FIS which were represented by the given Mapservices
tim@597:      * @param mapServices the Mapservices which should determine the FIS.
tim@597:      * @return all FIS which where represented my the given Mapservices.
tim@597:      */
tim@597:     protected Collection<FIS> getFIS(Collection<MapService> mapServices)
tim@597:                                               throws MetaDataServiceException{
tim@597:         log.debug("MetaDataService.getFIS ==> MapServices");
tim@597:         Collection<FIS> resultValue = null;
tim@597:         if (mapServices != null && !mapServices.isEmpty()){
tim@597:             try {
tim@597:                 
tim@597:                 String mapServiceNames = "";
tim@597:                 Iterator<MapService> mit = mapServices.iterator(); 
tim@597:                 while(mit.hasNext()){
tim@597:                     if (mapServiceNames.length() > 0){
tim@597:                         mapServiceNames += " , ";
tim@597:                     }
tim@604:                     mapServiceNames += "'"+mit.next().getID()+"'";
tim@597:                 }
tim@597:                 
tim@597:                 QueryExecutor queryExecutor = QueryExecutorFactory
tim@597:                                                .getInstance()
tim@597:                                                .getQueryExecutor();
tim@597:                 Collection<Result> result = queryExecutor.executeQuery(
tim@597:                                                   MAPSERVICES_HAS_FIS_QUERY_ID,
tim@597:                                                   new String[]{mapServiceNames});
tim@597:                 if (result != null){
tim@597:                     resultValue = new ArrayList<FIS>(result.size());
tim@597:                     Iterator<Result> it = result.iterator();
tim@597:                     while (it.hasNext()){
tim@597:                         Result value = it.next();
tim@606:                         String fisId = value.getString(0).trim();
tim@604:                         String mapServiceID = value.getString(1).trim();
tim@604:                        
tim@597:                         // FIRST LOOK IF ONE MAPSERVICE REPRESENTS ONLY ONE PARAM
tim@604:                         Collection<Result> result2 = queryExecutor.executeQuery(
tim@604:                                 MAPSERVICES_HAS_PARAMETER_QUERY_ID,
tim@604:                                 new String[]{"'"+mapServiceID+"'"});
tim@597:                         Collection<Parameter> parameter = null;
tim@604:                         if (result2 != null && result2.size() == 1){
tim@604:                             Iterator<Result> it2 = result2.iterator();
tim@604:                             parameter = new ArrayList<Parameter>(1);
tim@604:                             while (it2.hasNext()){
tim@604:                                 Result parameterValue = it2.next();
tim@604:                                 String parameterID = parameterValue.getString(0)
tim@604:                                                                    .trim();
tim@604:                                 parameter.add(new DefaultParameter(parameterID, 
tim@604:                                                                    parameterID));
tim@604:                             }
tim@604:                         }else{
tim@604:                             // IF FALSE LOOK IF THE GIVEN LAYERs TO AN MAPSERVICE
tim@604:                             // REPRESENTS DIFFERENT PARAMS
tim@604:                             MapService service = this.getMapService(mapServices,
tim@604:                                                                     mapServiceID);
tim@604:                             Collection<Layer> layer = service.getLayer();
tim@604:                             if (layer != null && !layer.isEmpty()){
tim@604:                                 String layerQueryString = 
tim@604:                                     this.createLayerQueryString(layer);
tim@604:                                 Collection<Result> parameterResult = 
tim@604:                                     queryExecutor.executeQuery(
tim@604:                                         MAPSERVICES_HAS_PARAMETER_USING_LAYER_QUERY_ID,
tim@604:                                         new String[]{"'"+mapServiceID+"'",
tim@604:                                                      layerQueryString});
tim@604:                                 if (parameterResult != null && 
tim@604:                                     !parameterResult.isEmpty()){
tim@604:                                     Iterator<Result> it2 = parameterResult.iterator();
tim@604:                                     parameter = new ArrayList<Parameter>(parameterResult.size());
tim@604:                                     while (it2.hasNext()){
tim@604:                                         Result parameterValue = it2.next();
tim@604:                                         String parameterID = parameterValue.getString(0)
tim@604:                                                                            .trim();
tim@604:                                         parameter.add(new DefaultParameter(parameterID, 
tim@604:                                                                            parameterID));
tim@604:                                     }
tim@604:                                 }
tim@604:                             }
tim@604:                             
tim@604:                         }
tim@606:                         
tim@606:                         FIS fis = this.getFIS(resultValue, fisId);
tim@606:                         if (fis != null){
tim@606:                             if (parameter != null){
tim@606:                                 fis.addParameter(parameter);
tim@606:                             }
tim@606:                         }else{
tim@606:                             resultValue.add(new DefaultFIS(fisId, parameter));
tim@606:                         }
tim@597:                     }
tim@597:                 }
tim@597:             } catch (QueryException e) {
tim@597:                 log.error(e,e);
tim@597:                 throw new MetaDataServiceException("Cannot Query FIS from DB.");
tim@597:             }
tim@597:            
tim@597:         }
tim@597:         return resultValue;
tim@597:     }
tim@604:     
tim@604:     
tim@606:     private FIS getFIS (Collection<FIS> fis, String fisId){
tim@606:         
tim@606:         Iterator<FIS> it = fis.iterator();
tim@606:         while(it.hasNext()){
tim@606:             FIS tmpFIS = it.next();
tim@606:             if (tmpFIS.getID().equals(fisId)){
tim@606:                 return tmpFIS;
tim@606:             }
tim@606:         }
tim@606:         return null;
tim@606:     }
tim@606:     
tim@604:     private MapService getMapService(Collection<MapService> mapServices,
tim@604:                                      String mapServiceID){
tim@604:         log.debug("MetaDataService.getMapService");
tim@604:         Iterator<MapService> it = mapServices.iterator();
tim@604:         while (it.hasNext()){
tim@604:             MapService service = it.next();
tim@604:             if (service.getID().equals(mapServiceID)){
tim@604:                 return service;
tim@604:             }
tim@604:         }
tim@604:         return null;
tim@604:     }
tim@604:     
tim@604:     private String createLayerQueryString(Collection<Layer> layer){
tim@604:         log.debug("MetaDataService.createLayerQueryString");
tim@604:         StringBuffer sb = new StringBuffer();;
tim@604:         Iterator<Layer> it = layer.iterator();
tim@604:         synchronized (sb) {
tim@604:             while (it.hasNext()){
tim@604:                 Layer l = it.next();
tim@604:                 if (!l.isGroupLayer()){
tim@604:                     sb.append(l.getID());
tim@604:                     if (it.hasNext()){
tim@604:                         sb.append(" , ");
tim@604:                     }
tim@604:                 }
tim@604:                 
tim@604:             }
tim@604:         }
tim@604:         String returnValue = sb.toString();
tim@604:         if (returnValue.endsWith(" , ")){
tim@604:             returnValue = returnValue.substring(0,returnValue
tim@604:                                                   .lastIndexOf(","))
tim@604:                                                   .trim();
tim@604:         }
tim@604:         return returnValue;
tim@604:     }
tim@597: 
tim@597:     /**
tim@597:      * @see de.intevation.artifactdatabase.DefaultService#setup(de.intevation.artifacts.ServiceFactory, java.lang.Object)
tim@597:      */
tim@597:     @Override
tim@597:     public void setup(ServiceFactory factory, Object globalContext) {
tim@597:         log.debug("MetaDataService.setup");
tim@597:         super.setup(factory, globalContext);
tim@597:         // TODO: Perhaps it is necessary to init the QueryIds here.
tim@597:     }
tim@597: 
tim@597: }