/** * ***************************************************************************** * Copyright 2013 Johannes Mitlmeier * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * **************************************************************************** */ package de.fub.agg2graph.agg.tiling; import de.fub.agg2graph.agg.AggConnection; import de.fub.agg2graph.agg.AggContainer; import de.fub.agg2graph.agg.AggNode; import de.fub.agg2graph.input.FileHandler; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Double; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.xml.sax.SAXException; /** * Cache management for tiles. * * @author Johannes Mitlmeier * */ public class TileCache { private static final Logger LOG = Logger.getLogger(TileCache.class.getName()); private Set<Tile<AggNode>> activeTiles; // private int cacheSize; private DefaultCachingStrategy dcs; private AggContainer agg; // private boolean inMemory = true; public TileCache(DefaultCachingStrategy dcs, AggContainer agg, int cacheSize) { this.dcs = dcs; this.agg = agg; // this.cacheSize = cacheSize; activeTiles = new HashSet<Tile<AggNode>>(); } public AggNode loadNode(String ID) { try { return dcs.tm.getNodeByFullID(ID); } catch (ParserConfigurationException e) { LOG.log(Level.SEVERE, e.getMessage(), e); } catch (SAXException e) { LOG.log(Level.SEVERE, e.getMessage(), e); } catch (IOException e) { LOG.log(Level.SEVERE, e.getMessage(), e); } return null; } public boolean isTileLoaded(Tile<AggNode> tile) { return activeTiles.contains(tile); } /** * Load a tile from storage to this cache making it available in memory. * * @param tile * @throws ParserConfigurationException * @throws SAXException * @throws IOException */ public void loadTile(Tile<AggNode> tile, boolean force) throws ParserConfigurationException, SAXException, IOException { if (!force && (isTileLoaded(tile) || dcs.getTc().isInMemory() || dcs.getTm().isEmpty())) { tile.setLoaded(true); activeTiles.add(tile); return; } // drill down Queue<Tile<AggNode>> tileQueue = new LinkedList<Tile<AggNode>>(); tileQueue.add(tile); Tile<AggNode> currentTile; File file; while (!tileQueue.isEmpty()) { currentTile = tileQueue.poll(); if (!force && isTileLoaded(currentTile)) { continue; } // check if this is the saved tile size file = new File(agg.getDataSource().getAbsoluteFile() + File.separator + String.format("%s.xml", currentTile.getID())); if (!file.exists()) { currentTile.split(); tileQueue.addAll(currentTile.getChildren()); } else { /* * We can read that file. Order is important here to prevent * from infinite recursion // upon adding nodes while reading * the XML representation. */ activeTiles.add(currentTile); XMLSerializationReader.loadNodes(file, agg); } } } public void loadTile(Tile<AggNode> tile) throws ParserConfigurationException, SAXException, IOException { loadTile(tile, false); } /** * Remove all nodes of one tile from memory. * * @param tile */ public void unloadTile(Tile<AggNode> tile) { ArrayList<AggNode> nodeListCopy = new ArrayList<AggNode>(tile.getElements().size()); nodeListCopy.addAll(tile.getElements()); for (AggNode node : nodeListCopy) { for (AggConnection inConn : node.getIn()) { inConn.unloadFrom(); } for (AggConnection outConn : node.getOut()) { outConn.unloadTo(); } dcs.getTm().addConnectionCounter(-node.getOut().size()); agg.removeNodeSilently(node); } activeTiles.remove(tile); } /** * Remove the data structures in memory as well as deleting all files * attached to this TileCache, thus somewhat a hard reset. * * @return */ public boolean remove() { // clear memory List<Tile<AggNode>> tileCopy = new ArrayList<Tile<AggNode>>( activeTiles.size()); tileCopy.addAll(activeTiles); for (Tile<AggNode> tile : tileCopy) { unloadTile(tile); } // remove the files return !agg.getDataSource().getAbsoluteFile().exists() || FileHandler.removeDirectory(agg.getDataSource() .getAbsoluteFile()); } /** * Save a tile to disk. * * @param tile * @throws ParserConfigurationException * @throws TransformerException */ public void saveTile(Tile<AggNode> tile) throws ParserConfigurationException, TransformerException { agg.getDataSource().mkdirs(); if (tile.isLeaf) { File outFile = new File(agg.getDataSource().getAbsoluteFile() + File.separator + String.format("%s.xml", tile.getID())); LOG.log(Level.INFO, "saving tile {0} to {1}", new Object[]{tile, outFile.getAbsolutePath()}); XMLSerializationWriter xmlWriter = new XMLSerializationWriter( outFile); for (AggNode node : tile.getElements()) { LOG.log(Level.INFO, "serializing {0}", node); xmlWriter.writeNode(node); } xmlWriter.close(); unloadTile(tile); } else { for (Tile<AggNode> subTile : tile.getChildren()) { saveTile(subTile); } } } /** * Make sure an area is loaded by loading all tiles contained (even partly) * in that area. * * @param areaRect * @throws ParserConfigurationException * @throws SAXException * @throws IOException */ public void loadArea(Rectangle2D.Double areaRect) throws ParserConfigurationException, SAXException, IOException { // drill down Queue<Tile<AggNode>> tileQueue = new LinkedList<Tile<AggNode>>(); tileQueue.add(dcs.getTm().getRoot()); Tile<AggNode> currentTile; Rectangle2D.Double overlap; while (!tileQueue.isEmpty()) { currentTile = tileQueue.poll(); // check overlap overlap = (Double) currentTile.size.createIntersection(areaRect); if (overlap.width == 0) { continue; } else if (overlap.equals(currentTile.getSize())) { loadTile(currentTile); } else { tileQueue.addAll(currentTile.getChildren()); } } } public void setAggContainer(AggContainer agg) { this.agg = agg; } public boolean isInMemory() { return agg == null || agg.getDataSource() == null || !agg.getDataSource().exists() || !agg.getDataSource().isDirectory() || agg.getDataSource().list().length == 0; } public void clear() { activeTiles.clear(); } public Set<Tile<AggNode>> getActiveTiles() { return Collections.<Tile<AggNode>>unmodifiableSet(activeTiles); } }