sascha@1749: package de.intevation.flys.artifacts.services;
sascha@1749: 
sascha@1749: import de.intevation.artifacts.CallMeta;
sascha@1749: import de.intevation.artifacts.GlobalContext;
sascha@1749: 
sascha@1749: import de.intevation.artifacts.common.utils.XMLUtils;
sascha@1749: 
sascha@1749: import de.intevation.flys.artifacts.cache.CacheFactory;
sascha@1749: 
sascha@1749: import de.intevation.flys.backend.SessionHolder;
sascha@1749: 
sascha@1749: import de.intevation.flys.model.CrossSection;
sascha@1749: import de.intevation.flys.model.CrossSectionLine;
sascha@1749: 
sascha@1749: import java.util.ArrayDeque;
sascha@1749: import java.util.Deque;
sascha@1749: import java.util.List;
sascha@1749: import java.util.Map;
sascha@1749: import java.util.NavigableMap;
sascha@1749: 
sascha@1749: import java.util.concurrent.ConcurrentSkipListMap;
sascha@1749: 
sascha@1749: import net.sf.ehcache.Cache;
sascha@1749: 
sascha@1749: import org.apache.log4j.Logger;
sascha@1749: 
sascha@1749: import org.hibernate.Query;
sascha@1749: import org.hibernate.Session;
sascha@1749: 
sascha@1749: import org.w3c.dom.Document;
sascha@1749: import org.w3c.dom.Element;
sascha@1749: import org.w3c.dom.NodeList;
sascha@1749: 
sascha@1749: public class CrossSectionKMService
sascha@1749: extends      FLYSService
sascha@1749: {
sascha@1749:     private static Logger logger =
sascha@1749:         Logger.getLogger(CrossSectionKMService.class);
sascha@1749: 
sascha@1749:     public static final String CACHE_NAME = "cross-section-kms";
sascha@1749: 
sascha@1749:     public CrossSectionKMService() {
sascha@1749:     }
sascha@1749: 
sascha@1749:     @Override
sascha@1749:     public Document doProcess(
sascha@1749:         Document      data,
sascha@1749:         GlobalContext globalContext,
sascha@1749:         CallMeta      callMeta
sascha@1749:     ) {
sascha@1749:         logger.debug("CrossSectionKMService.doProcess");
sascha@1749: 
sascha@1749:         NodeList crossSectionNodes =
sascha@1749:             data.getElementsByTagName("cross-section");
sascha@1749: 
sascha@1749:         Cache cache = CacheFactory.getCache(CACHE_NAME);
sascha@1749: 
sascha@1749:         Document document = XMLUtils.newDocument();
sascha@1749: 
sascha@1749:         Element all = document.createElement("cross-sections");
sascha@1749: 
sascha@1749:         for (int i = 0, CS = crossSectionNodes.getLength(); i < CS; ++i) {
sascha@1749:             Element crossSectionElement = (Element)crossSectionNodes.item(i);
sascha@1749: 
sascha@1749:             String idString = crossSectionElement.getAttribute("id");
sascha@1749:             String kmString = crossSectionElement.getAttribute("km");
sascha@1749:             String neighborsString = crossSectionElement.getAttribute("n");
sascha@1749: 
sascha@1749:             if (idString.length() == 0 || kmString.length() == 0) {
sascha@1749:                 logger.debug("missing attributes in cross-section element");
sascha@1749:                 continue;
sascha@1749:             }
sascha@1749: 
sascha@1749:             double  km;
sascha@1749:             Integer crossSectionId;
sascha@1749:             int     N = 2;
sascha@1749: 
sascha@1749:             try {
sascha@1749:                 km             = Double.parseDouble(kmString);
sascha@1749:                 crossSectionId = Integer.valueOf(idString);
sascha@1749: 
sascha@1749:                 if (neighborsString.length() > 0) {
sascha@1749:                     N = Integer.parseInt(neighborsString);
sascha@1749:                 }
sascha@1749:             }
sascha@1749:             catch (NumberFormatException nfe) {
sascha@1749:                 logger.debug("converting number failed", nfe);
sascha@1749:                 continue;
sascha@1749:             }
sascha@1749: 
sascha@1749:             NavigableMap<Double, Integer> map;
sascha@1749: 
sascha@1749:             if (cache == null) {
sascha@1749:                 map = getUncached(crossSectionId);
sascha@1749:             }
sascha@1749:             else {
sascha@1749:                 net.sf.ehcache.Element element = cache.get(crossSectionId);
sascha@1749:                 if (element == null) {
sascha@1749:                     map = getUncached(crossSectionId);
sascha@1749:                     if (map != null) {
sascha@1749:                         element = new net.sf.ehcache.Element(
sascha@1749:                             crossSectionId, map);
sascha@1749:                         cache.put(element);
sascha@1749:                     }
sascha@1749:                 }
sascha@1749:                 else {
sascha@1749:                     map = (NavigableMap<Double, Integer>)element.getValue();
sascha@1749:                 }
sascha@1749:             }
sascha@1749: 
sascha@1749:             if (map == null) {
sascha@1749:                 logger.debug("cannot find cross section " + crossSectionId);
sascha@1749:                 continue;
sascha@1749:             }
sascha@1749: 
sascha@1749:             Deque<Map.Entry<Double, Integer>> result =
sascha@1749:                 nearestNeighbors(map, km, N);
sascha@1749: 
sascha@1749:             if (!result.isEmpty()) {
sascha@1749:                 Element csE = document.createElement("cross-section");
sascha@1749:                 csE.setAttribute("id", idString);
sascha@1749:                 for (Map.Entry<Double, Integer> entry: result) {
sascha@1749:                     Element lineE = document.createElement("line");
sascha@1749:                     lineE.setAttribute(
sascha@1749:                         "line-id", String.valueOf(entry.getValue()));
sascha@1749:                     lineE.setAttribute(
sascha@1749:                         "km", String.valueOf(entry.getKey()));
sascha@1749:                     csE.appendChild(lineE);
sascha@1749:                 }
sascha@1749:                 all.appendChild(csE);
sascha@1749:             }
sascha@1749:         }
sascha@1749: 
sascha@1749:         document.appendChild(all);
sascha@1749: 
sascha@1749:         return document;
sascha@1749:     }
sascha@1749: 
sascha@1749:     public static Deque<Map.Entry<Double, Integer>> nearestNeighbors(
sascha@1749:         NavigableMap<Double, Integer> map,
sascha@1749:         double                        km,
sascha@1749:         int                           N
sascha@1749:     ) {
sascha@1749:         Deque<Map.Entry<Double, Integer>> result =
sascha@1749:             new ArrayDeque<Map.Entry<Double, Integer>>(2*N);
sascha@1749: 
sascha@1749:         int i = 0;
sascha@1749:         for (Map.Entry<Double, Integer> entry:
sascha@1749:              map.headMap(km, false).descendingMap().entrySet()) {
sascha@1749:             if (i++ >= N) {
sascha@1749:                 break;
sascha@1749:             }
sascha@1749:             result.addFirst(entry);
sascha@1749:         }
sascha@1749: 
sascha@1749:         i = 0;
sascha@1749:         for (Map.Entry<Double, Integer> entry:
sascha@1749:              map.tailMap(km, false).entrySet()) {
sascha@1749:             if (i++ >= N) {
sascha@1749:                 break;
sascha@1749:             }
sascha@1749:             result.addLast(entry);
sascha@1749:         }
sascha@1749: 
sascha@1749:         return result;
sascha@1749:     }
sascha@1749: 
sascha@1749:     public static NavigableMap<Double, Integer> getUncached(
sascha@1749:         Integer crossSectionId
sascha@1749:     ) {
sascha@1749:         NavigableMap<Double, Integer> result =
sascha@1749:             new ConcurrentSkipListMap<Double, Integer>();
sascha@1749: 
sascha@1749:         Session session = SessionHolder.HOLDER.get();
sascha@1749:         Query query = session.createQuery(
sascha@1749:             "from CrossSection where id=:id");
sascha@1749:         query.setParameter("id", crossSectionId);
sascha@1749: 
sascha@1749:         List<CrossSection> crossSections = query.list();
sascha@1749:         if (crossSections.isEmpty()) {
sascha@1749:             return null;
sascha@1749:         }
sascha@1749: 
sascha@1749:         CrossSection crossSection = crossSections.get(0);
sascha@1749:         List<CrossSectionLine> lines = crossSection.getLines();
sascha@1749: 
sascha@1749:         for (CrossSectionLine line: lines) {
sascha@1749:             Double  km = line.getKm().doubleValue();
sascha@1749:             Integer id = line.getId();
sascha@1749:             result.put(km, id);
sascha@1749:         }
sascha@1749: 
sascha@1749:         return result;
sascha@1749:     }
sascha@1749: }
sascha@1749: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :