/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2001 Michael Powers This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.access; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.util.Enumeration; import net.wotonomy.foundation.NSArray; import net.wotonomy.foundation.NSDictionary; import net.wotonomy.foundation.NSMutableArray; import net.wotonomy.foundation.NSMutableDictionary; import net.wotonomy.foundation.NSPropertyListSerialization; /** * An EOModel is a set of entities and stored procedures, along with a * connection dictionary to connect to a database. * * @author ezamudio@nasoft.com * @author $Author: cgruber $ * @version $Revision: 894 $ */ public class EOModel { protected static final String IDX_NAME = "index.eomodeld"; // This array contains dictionaries with "className" and "name" protected NSMutableArray _entities = new NSMutableArray(); protected NSMutableDictionary _entitiesByName = new NSMutableDictionary(); protected NSMutableDictionary _entitiesByClass = new NSMutableDictionary(); protected NSDictionary _connectionDictionary = NSDictionary.EmptyDictionary; protected String _adaptorName = "JDBC"; protected NSMutableDictionary _prototypesByName = new NSMutableDictionary(); protected NSMutableDictionary _storedProcedures = new NSMutableDictionary(); protected NSMutableArray _storedProcedureNames = new NSMutableArray(); protected NSDictionary _userInfo = NSDictionary.EmptyDictionary; protected String _name; protected String _path; protected NSDictionary _internalInfo = NSDictionary.EmptyDictionary; protected EOModelGroup _group; public EOModel() { super(); } public EOModel(NSDictionary dict, Object o) { this(); } public EOModel(String path) { this(); File f = new File(path); _path = f.getAbsolutePath(); if (!(f.exists() && f.isDirectory())) throw new IllegalArgumentException("The path is invalid (" + f + ")"); _name = f.getName(); f = new File(f, "index.eomodeld"); if (!(f.exists() && f.isFile())) throw new IllegalArgumentException("Cannot find " + f); String x = null; try { FileInputStream in = new FileInputStream(f); byte[] b = new byte[in.available()]; in.read(b); in.close(); x = new String(b); } catch (IOException e) { throw new IllegalArgumentException("Cannot read index.eomodeld"); } NSDictionary d = NSPropertyListSerialization.dictionaryForString(x); String version = (String) d.objectForKey("EOModelVersion"); if (version == null || !version.startsWith("2.")) throw new IllegalArgumentException("Invalid eomodel version: " + version); setAdaptorName((String) d.objectForKey("adaptorName")); setConnectionDictionary((NSDictionary) d.objectForKey("connectionDictionary")); _entities = ((NSArray) d.objectForKey("entities")).mutableClone(); if (d.objectForKey("storedProcedures") != null) _storedProcedureNames.addObjectsFromArray((NSArray) d.objectForKey("storedProcedures")); if (d.objectForKey("internalInfo") != null) _internalInfo = (NSDictionary) d.objectForKey("internalInfo"); if (d.objectForKey("userInfo") != null) _userInfo = (NSDictionary) d.objectForKey("userInfo"); entityNamed("EOPrototypes"); } public void setConnectionDictionary(NSDictionary dict) { _connectionDictionary = dict; } public NSDictionary connectionDictionary() { return _connectionDictionary; } public void addEntity(EOEntity ent) { _entitiesByName.setObjectForKey(ent, ent.name()); _entitiesByClass.setObjectForKey(ent, ent.className()); } public void removeEntity(EOEntity ent) { _entitiesByName.removeObjectForKey(ent.name()); _entitiesByClass.removeObjectForKey(ent.className()); } public void addStoredProcedure(EOStoredProcedure proc) { if (!_storedProcedureNames.containsObject(proc)) _storedProcedureNames.addObject(proc); _storedProcedures.setObjectForKey(proc, proc.name()); } public void removeStoredProcedure(EOStoredProcedure proc) { _storedProcedureNames.removeObject(proc.name()); _storedProcedures.removeObjectForKey(proc.name()); } public EOStoredProcedure storedProcedureNamed(String name) { EOStoredProcedure proc = (EOStoredProcedure) _storedProcedures.objectForKey(name); // if we can't find it, check if we should load it if (proc == null && _path != null && _storedProcedureNames.containsObject(name)) { // try to read it from file File f = new File(new File(_path), name + ".storedProcedure"); if (!f.exists()) return null; String sdict = null; try { FileInputStream fin = new FileInputStream(f); byte[] b = new byte[fin.available()]; fin.read(b); fin.close(); sdict = new String(b); } catch (IOException ex) { return null; } NSDictionary plist = NSPropertyListSerialization.dictionaryForString(sdict); if (plist == null) throw new IllegalArgumentException( "File " + f + " does not contain a valid stored procedure property list."); proc = new EOStoredProcedure(plist, this); // add it to our collection _storedProcedures.setObjectForKey(proc, proc.name()); } return proc; } public NSArray storedProcedures() { if (_storedProcedures.count() < _storedProcedureNames.count()) { for (int i = 0; i < _storedProcedureNames.count(); i++) storedProcedureNamed((String) _storedProcedureNames.objectAtIndex(i)); } return _storedProcedures.allValues(); } public NSArray storedProcedureNames() { return _storedProcedures.allKeys(); } public void setAdaptorName(String value) { _adaptorName = value; } public String adaptorName() { return _adaptorName; } public NSArray entities() { if (_entitiesByName.count() >= _entities.count()) return _entitiesByName.allValues(); NSMutableArray es = new NSMutableArray(); for (int i = 0; i < _entities.count(); i++) { NSDictionary d = (NSDictionary) _entities.objectAtIndex(i); es.addObject(entityNamed((String) d.objectForKey("name"))); } return es; } public NSArray entityNames() { if (_entitiesByName.count() >= _entities.count()) return _entitiesByName.allKeys(); NSMutableArray names = new NSMutableArray(); for (int i = 0; i < _entities.count(); i++) { NSDictionary d = (NSDictionary) _entities.objectAtIndex(i); names.addObject(d.objectForKey("name")); } return names; } public EOEntity entityNamed(String name) { EOEntity e = (EOEntity) _entitiesByName.objectForKey(name); if (e == null && path() != null) { boolean exists = false; for (int i = 0; i < _entities.count(); i++) { NSDictionary d = (NSDictionary) _entities.objectAtIndex(i); if (d.objectForKey("name").equals(name)) exists = true; } if (!exists) return null; File f = new File(new File(path()), name + ".plist"); if (!(f.exists() && f.isFile())) throw new IllegalArgumentException("Cannot find " + name + ".plist"); String s = null; try { FileInputStream fin = new FileInputStream(f); byte[] b = new byte[fin.available()]; fin.read(b); fin.close(); s = new String(b); } catch (IOException ex) { throw new IllegalArgumentException("Cannot read " + f); } NSDictionary d = NSPropertyListSerialization.dictionaryForString(s); if (d == null) throw new IllegalArgumentException("Cannot parse dictionary for " + f); e = new EOEntity(d, this); _entitiesByName.setObjectForKey(e, e.name()); } return e; } public String name() { return _name; } public String path() { return _path; } public void setModelGroup(EOModelGroup group) { _group = group; } public EOModelGroup modelGroup() { return _group; } public void setUserInfo(NSDictionary dict) { _userInfo = dict; } public NSDictionary userInfo() { return _userInfo; } public void write() { } public void writeToFile(String path) { if (!path.endsWith(".eomodeld")) path += ".eomodeld"; File f = new File(path); if (f.exists()) { if (f.isDirectory()) { File[] kids = f.listFiles(); for (int i = 0; i < kids.length; i++) { String fname = kids[i].getName(); if (kids[i].isFile() && (fname.endsWith(".plist") || fname.endsWith(".eomodeld") || fname.endsWith(".fspec") || fname.endsWith(".storedProcedure"))) kids[i].delete(); } } else f.delete(); } else f.mkdirs(); File kid = new File(f, "index.eomodeld"); // encode the index file NSMutableDictionary d = new NSMutableDictionary(); d.setObjectForKey(_adaptorName, "adaptorName"); d.setObjectForKey("2.1", "EOModelVersion"); d.setObjectForKey(_connectionDictionary, "connectionDictionary"); if (_internalInfo != null && _internalInfo.count() > 0) d.setObjectForKey(_internalInfo, "internalInfo"); if (_userInfo != null && _userInfo.count() > 0) d.setObjectForKey(_userInfo, "userInfo"); if (_storedProcedureNames.count() > 0) d.setObjectForKey(_storedProcedureNames, "storedProcedures"); // encode the entity list entities(); storedProcedures(); NSMutableArray arr = new NSMutableArray(_entitiesByName.count()); Enumeration enumeration = _entitiesByName.keyEnumerator(); NSMutableDictionary md = new NSMutableDictionary(); while (enumeration.hasMoreElements()) { String key = (String) enumeration.nextElement(); EOEntity ent = (EOEntity) _entitiesByName.objectForKey(key); md.removeAllObjects(); ent.encodeIntoPropertyList(md); File plist; NSDictionary fetchSpecs = (NSDictionary) md.objectForKey("fetchSpecificationDictionary"); if (fetchSpecs != null) { if (fetchSpecs.count() > 0) { plist = new File(f, key + ".fspec"); String ps = NSPropertyListSerialization .stringForPropertyList(md.objectForKey("fetchSpecificationDictionary")); try { PrintStream stream = new PrintStream(new FileOutputStream(plist)); stream.println(ps); stream.close(); } catch (IOException ex) { } } md.removeObjectForKey("fetchSpecificationDictionary"); } plist = new File(f, key + ".plist"); String ps = NSPropertyListSerialization.stringForPropertyList(md); try { PrintStream stream = new PrintStream(new FileOutputStream(plist)); stream.println(ps); stream.close(); } catch (IOException ex) { } // add to the entity list for the index arr.addObject(new NSDictionary(new Object[] { ent.name(), ent.className() }, new Object[] { "name", "className" })); } d.setObjectForKey(arr, "entities"); // write the index file String s = NSPropertyListSerialization.stringForPropertyList(d); try { PrintStream stream = new PrintStream(new FileOutputStream(kid)); stream.println(s); stream.close(); } catch (IOException ex) { throw new RuntimeException("Cannot write index.eomodeld"); } // write the stored procedures for (int i = 0; i < _storedProcedureNames.count(); i++) { EOStoredProcedure proc = (EOStoredProcedure) _storedProcedures .objectForKey(_storedProcedureNames.objectAtIndex(i)); kid = new File(f, proc.name() + ".storedProcedure"); d.removeAllObjects(); proc.encodeIntoPropertyList(d); s = NSPropertyListSerialization.stringForPropertyList(d); try { PrintStream stream = new PrintStream(new FileOutputStream(kid)); stream.println(s); stream.close(); } catch (IOException ex) { throw new RuntimeException("Cannot write " + f); } } _path = f.getAbsolutePath(); } } /* * $Log$ Revision 1.2 2006/02/16 16:47:14 cgruber Move some classes in to * "internal" packages and re-work imports, etc. * * Also use UnsupportedOperationExceptions where appropriate, instead of * WotonomyExceptions. * * Revision 1.1 2006/02/16 13:19:57 cgruber Check in all sources in * eclipse-friendly maven-enabled packages. * * Revision 1.8 2005/05/11 15:21:53 cgruber Change enum to enumeration, since * enum is now a keyword as of Java 5.0 * * A few other comments in the code. * * Revision 1.7 2003/08/13 20:46:12 chochos entityNamed() only throws if the * searched entity is in the entities array and not in a file. * * Revision 1.6 2003/08/12 01:45:04 chochos added some code to handle prototypes * * Revision 1.5 2003/08/11 19:38:27 chochos Can now read from a file and * re-write to another file. * * Revision 1.4 2003/08/11 18:20:08 chochos saving to a file seems to work now. * * Revision 1.3 2003/08/08 02:16:55 chochos can now read stored procedures from * file. * * Revision 1.2 2003/08/08 00:37:00 chochos add stored procedure functionality * * Revision 1.1 2003/08/07 02:42:28 chochos EOModel can read an .eomodeld file. * EOModelGroup doesn't do much for now. * */