package adecWatt.model; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import misc.Config; import misc.Log; import misc.Util; import static misc.Config.FS; import adecWatt.control.AdecWattManager; import adecWatt.model.unit.Building; import adecWatt.model.xml.XmlUnit; public class PermanentDB { // ======================================== static public final File dataDir = Config.findDataDir (); static public enum UnitLocation { Server, Local, Zip }; static public final String localToken = "local"; static public final String serverToken = "server"; static public final String cacheToken = "cache"; static public final List localRemoteDir = Arrays.asList (localToken, serverToken); static public final String lptExtention = "lpt"; static public final String lpzExtention = "lpz"; static public final String backExtention = "back"; static public final String[] exportExtention = { lpzExtention }; // ======================================== private AdecWatt adecWatt; private ImageDB iconDB; private ImageDB imageDB; private String powerPlugId, powerSocketId; public String getPowerPlugId () { return powerPlugId; } public String getPowerSocketId () { return powerSocketId; } public ArrayList getAllBuildings () { ArrayList result = new ArrayList (); for (Enumeration enumUnitNode = getRootByName (Prop.PropBuilding).getUnitNode ().breadthFirstEnumeration (); enumUnitNode.hasMoreElements(); ) { Building building = (Building) ((UnitNode) enumUnitNode.nextElement ()).getUnit (); if (building.isUncompleted (null)) continue; result.add (building); } return result; } public PermanentDB (AdecWatt adecWatt) { this.adecWatt = adecWatt; iconDB = adecWatt.getIconDB (); imageDB = adecWatt.getImageDB (); } // ======================================== // Unit // ======================================== HashSet> allUnit = new HashSet> (); Hashtable> idUnit = new Hashtable> (); Hashtable> namedRoots = new Hashtable> (); public boolean isReferedUnit (String unitId) { return idUnit.containsKey (unitId); } public Unit getUnitById (String unitId) { return idUnit.get (unitId); } public Unit getRootByName (String unitName) { return namedRoots.get (unitName); } public Set getReferedIds () { return idUnit.keySet (); } public boolean isModifiedUnit () { for (Unit unit : allUnit) if (unit.story.isModified ()) return true; return false; } // ======================================== public void reload () { powerSocketId = powerPlugId = null; allUnit.clear (); idUnit.clear (); namedRoots.clear (); for (String srcDir : localRemoteDir) for (File file : getAllXmlFiles (srcDir)) try { if (file.length () == 0) continue; Log.writeLog ("PermanentDB", "load "+srcDir+"/"+file.getName ()); FileInputStream fileInputStream = new FileInputStream (file); Unit unit = Unit.getInstanceFromStream (adecWatt, fileInputStream, file, serverToken.equals (srcDir) ? UnitLocation.Server : UnitLocation.Local); fileInputStream.close (); if (isReferedUnit (unit.getId ())) { System.err.println (unit.getName ()+" already recorded : skip "+file); // ignore continue; } add (unit); } catch (Exception e) { System.err.println ("XXX: "+file+" error: "+e); e.printStackTrace (); } for (Unit unit : allUnit) unit.updateXmlParent (); } public void newNamePlan (TreeMap iconMap, TreeMap imageMap) { HashSet> visited = new HashSet> (); for (Unit unit : allUnit) unit.newNamePlan (visited, iconMap, imageMap); } public void add (Unit unit) { String unitId = unit.getId (); try { allUnit.remove (idUnit.get (unitId)); } catch (Exception e) { } allUnit.add (unit); idUnit.put (unitId, unit); adecWatt.getUser ().updateDataId (unitId); if ("powerPlug".equals (unit.getName ())) powerPlugId = unitId; if ("powerSocket".equals (unit.getName ())) powerSocketId = unitId; unit.enableStory (); } public void forget (Unit unit) { adecWatt.broadcastDisplay (AdecWattManager.actionClose, unit); idUnit.remove (unit.getId ()); allUnit.remove (unit); unit.disableStory (); // XXX et si "powerPlug" ou "powerSocket" ? } public Unit reload (Unit unit) { for (String srcDir : localRemoteDir) { try { File file = getXmlFile (new File (dataDir, srcDir), unit.getFileName ()); FileInputStream fileInputStream = new FileInputStream (file); Unit newUnit = Unit.getInstanceFromStream (adecWatt, fileInputStream, file, serverToken.equals (srcDir) ? UnitLocation.Server : UnitLocation.Local); fileInputStream.close (); return newUnit; } catch (Exception e) { } } return null; } public void remove (Unit unit) { if (unit.file != null) Util.backup (unit.file, Util.getExtention (unit.file), backExtention); forget (unit); } public void renameVisitor (HashSet> units) { TreeMap translateMap = new TreeMap (); String pattern = String.format ("%03d-", User.visitorId); User user = adecWatt.getUser (); for (Unit unit : units) { String unitId = unit.getId (); if (!unitId.startsWith (pattern)) continue; translateMap.put (unitId, user.getDataId ()); } if (translateMap.size () < 1) return; for (String unitId : translateMap.keySet ()) { Unit unit = idUnit.get (unitId); if (unit.file != null) Util.backup (unit.file, Util.getExtention (unit.file), backExtention); idUnit.remove (unitId); String newUnitId = translateMap.get (unitId); unit.changeId (newUnitId); idUnit.put (newUnitId, unit); } for (Unit unit : allUnit) if (unit.renameUnits (translateMap)) // YYY voir save ? unit.story.markNotSaved (); } public void promote (HashSet> units) { if (units.size () == 0) return; for (Unit unit : units) unit.promote (); adecWatt.broadcastUpdate (AdecWatt.BroadcastUnitRoots); } public void renameImages (TreeMap translateMap) { for (Unit unit : allUnit) if (unit.renameImages (translateMap)) // YYY voir save ? unit.story.markNotSaved (); } // ======================================== static public final File getXmlFile (String name) { for (String srcDir : localRemoteDir) try { File file = getXmlFile (new File (dataDir, srcDir), name); if (file != null) return file; } catch (Exception e) { } return null; } static public final File getXmlFile (File dir, String name) { File test = new File (dir, name); if (test.exists ()) return test; for (File subDir : getSubDirs (dir)) { test = getXmlFile (subDir, name); if (test != null) return test; } return null; } static public final File[] getXmlFiles (File dir) { return dir.listFiles (new FileFilter () { public boolean accept (File file) { return file.isFile () && !file.isHidden () && lptExtention.equals (Util.getExtention (file)); } }); } static public final File[] getSubDirs (File dir) { return dir.listFiles (new FileFilter () { public boolean accept (File file) { return file.isDirectory () && ! file.isHidden (); } }); } public boolean isFileNameExists (String baseName) { for (String srcDir : localRemoteDir) try { if (isFileExists (new File (dataDir, srcDir), baseName)) return true; } catch (Exception e) { } return false; } public boolean isFileExists (File dir, String baseName) { for (File file : getXmlFiles (dir)) { String fileName = file.getName (); if (fileName.startsWith (baseName) && lptExtention.equals (fileName.substring (1+baseName.length ()))) return true; } for (File subDir : getSubDirs (dir)) if (isFileExists (subDir, baseName)) return true; return false; } // ======================================== static public ArrayList getAllXmlFiles (String srcDir) { ArrayList result = new ArrayList (); try { getAllXmlFiles (new File (dataDir, srcDir), result); } catch (Exception e) { } return result; } static public void getAllXmlFiles (File dir, ArrayList result) { for (File file : getXmlFiles (dir)) result.add (file); for (File subDir : getSubDirs (dir)) getAllXmlFiles (subDir, result); } // ======================================== static public File getInputFile (String name) { for (String srcDir : localRemoteDir) { File file = new File (dataDir, srcDir+FS+name); if (file.exists () && file.isFile ()) return file; } throw new IllegalArgumentException ("Can't find input "+name+"!"); } // ======================================== static public File getLocalFile (String name) { File file = (new File (dataDir, localToken+FS+name)); file.getParentFile ().mkdirs (); return file; } static public File getRemoteFile (String name) { File file = (new File (dataDir, serverToken+FS+name)); file.getParentFile ().mkdirs (); return file; } // ======================================== static public XmlUnit readXmlUnit (InputStream stream) { try { DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); Document document = documentBuilder.parse (stream); document.normalizeDocument (); return new XmlUnit (document.getFirstChild ()); } catch (Exception e) { Log.keepLastException ("PermanentDB::readDocument", e); throw new IllegalArgumentException ("Not XML file input."); } } // ======================================== static public Document newDocument () { try { DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance ().newDocumentBuilder (); Document document = documentBuilder.newDocument (); document.setXmlStandalone (true); return document; } catch (Exception e) { return null; } } // ======================================== static public void writeUnit (Unit unit, File file) { try { FileOutputStream fileOutputStream = new FileOutputStream (file); writeUnit (unit, fileOutputStream); fileOutputStream.close (); } catch (Exception e) { Log.keepLastException ("PermanentDB::writeDocument", e); } } static public void writeUnit (Unit unit, OutputStream stream) throws IOException, TransformerException { Document document = newDocument (); unit.getXml (document, document); Source source = new DOMSource (document); Result result = new StreamResult (stream); Transformer xformer = TransformerFactory.newInstance ().newTransformer (); xformer.setOutputProperty (OutputKeys.INDENT, "yes"); xformer.setOutputProperty ("{http://xml.apache.org/xslt}indent-amount", "2"); xformer.transform (source, result); stream.flush (); } public void addUnitZip (ZipOutputStream out, Unit unit) throws IOException, TransformerException { if (unit == null) return; ZipEntry ze = new ZipEntry (unit.getSaveDir ()+"/"+unit.getFileName ()); out.putNextEntry (ze); ByteArrayOutputStream stream = new ByteArrayOutputStream (); writeUnit (unit, stream); out.write (stream.toByteArray ()); out.closeEntry(); out.flush (); } public File getLastExport () { return Config.getFile ("LastExport"); } public File checkExportExtention (File file) { if (Util.getExtention (file) != null) return file; return new File (file.getParentFile (), Util.changeExtention (file.getName (), lpzExtention)); } public void exportFile (Unit unit, File file) { if (file == null) return; try { file = checkExportExtention (file); HashSet> unitLinks = new HashSet> (); TreeSet iconsLinks = new TreeSet (); TreeSet imagesLinks = new TreeSet (); unitLinks.add (unit); unit.getLocalLink (new HashSet> (), unitLinks, iconsLinks, imagesLinks); FileOutputStream fileOutputStream = new FileOutputStream (file); ZipOutputStream out = new ZipOutputStream (fileOutputStream); for (Unit unit2 : unitLinks) addUnitZip (out, unit2); // XXX prendre aussi les étiquettes dans icon/"user".lpi filté avec "^imageName^:.*" dans iconsLinks for (String iconName : iconsLinks) iconDB.addImageZip (out, iconName); // XXX prendre aussi les étiquettes dans image/"user".lpi filté avec "^imageName^:.*" dans imagesLinks for (String imageName : imagesLinks) imageDB.addImageZip (out, imageName); out.close(); fileOutputStream.close (); Config.setFile ("LastExport", file); } catch (Exception e) { e.printStackTrace (); } } public void importFile (File file) { if (file == null) return; try { file = checkExportExtention (file); ArrayList> newUnit = new ArrayList> (); byte[] tmp = new byte[1024*1024]; FileInputStream fileInputStream = new FileInputStream (file); ZipInputStream in = new ZipInputStream (fileInputStream); for (;;) { ZipEntry ze = in.getNextEntry (); if (ze == null) break; ByteArrayOutputStream out = new ByteArrayOutputStream (); for (;;) { int nb = in.read (tmp, 0, tmp.length); if (nb < 0) break; out.write (tmp, 0, nb); } out.flush (); in.closeEntry (); String[] path = ze.getName ().split ("/"); String dirName = path[0]; String fileName = path [1]; String fileExtention = Util.getExtention (fileName); ByteArrayInputStream dataIn = new ByteArrayInputStream (out.toByteArray ()); if (!iconDB.importFile (dirName, fileName, dataIn) && !imageDB.importFile (dirName, fileName, dataIn)) { if (lptExtention.equals (fileExtention)) newUnit.add (Unit.getInstanceFromStream (adecWatt, dataIn, null, UnitLocation.Zip)); else System.err.println ("XXX pb: "+fileName); } } fileInputStream.close (); for (Unit unit : newUnit) { String unitId = unit.getId (); if (isReferedUnit (unitId)) { Unit oldUnit = idUnit.get (unitId); forget (oldUnit); oldUnit.replace (unit); continue; } add (unit); } for (Unit unit : newUnit) { unit.updateXmlParent (); unit.story.markNotSaved (); } adecWatt.broadcastUpdate (AdecWatt.BroadcastUnitRoots); Config.setFile ("LastExport", file); } catch (Exception e) { e.printStackTrace (); } } // ======================================== }