http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java ---------------------------------------------------------------------- diff --git a/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java b/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java index 95f6d8e..4797b43 100644 --- a/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java +++ b/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/EOModelProcessor.java @@ -55,722 +55,720 @@ import org.apache.commons.logging.LogFactory; */ public class EOModelProcessor { - private static final Log logger = LogFactory.getLog(EOModelProcessor.class); - - protected Predicate prototypeChecker; - - public EOModelProcessor() { - prototypeChecker = new Predicate() { - - public boolean evaluate(Object object) { - if (object == null) { - return false; - } - - String entityName = object.toString(); - return entityName.startsWith("EO") && entityName.endsWith("Prototypes"); - } - }; - } - - /** - * @deprecated since 4.0 in favor of {@link #loadModeIndex(URL)}. - */ - @Deprecated - public Map loadModeIndex(String path) throws Exception { - return loadModeIndex(new File(path).toURI().toURL()); - } - - /** - * Returns index.eomodeld contents as a Map. - * - * @since 4.0 - */ - // TODO: refactor EOModelHelper to provide a similar method without loading - // all entity files in memory... here we simply copied stuff from - // EOModelHelper - public Map loadModeIndex(URL url) throws Exception { - - String urlString = url.toExternalForm(); - - if (!urlString.endsWith(".eomodeld")) { - url = new URL(urlString + ".eomodeld"); - } - - Parser plistParser = new Parser(); - InputStream in = new URL(url, "index.eomodeld").openStream(); - - try { - plistParser.ReInit(in); - return (Map) plistParser.propertyList(); - } finally { - in.close(); - } - } - - /** - * @deprecated since 4.0 in favor of {@link #loadEOModel(URL)}. - */ - @Deprecated - public DataMap loadEOModel(String path) throws Exception { - return loadEOModel(path, false); - } - - /** - * @deprecated since 4.0 in favor of {@link #loadEOModel(URL, boolean)}. - */ - @Deprecated - public DataMap loadEOModel(String path, boolean generateClientClass) throws Exception { - return loadEOModel(new File(path).toURI().toURL(), generateClientClass); - } - - /** - * Performs EOModel loading. - * - * @param url - * URL of ".eomodeld" directory. - */ - public DataMap loadEOModel(URL url) throws Exception { - return loadEOModel(url, false); - } - - /** - * Performs EOModel loading. - * - * @param url - * URL of ".eomodeld" directory. - * @param generateClientClass - * if true then loading of EOModel is java client classes aware - * and the following processing will work with Java client class - * settings of the EOModel. - */ - public DataMap loadEOModel(URL url, boolean generateClientClass) throws Exception { - EOModelHelper helper = makeHelper(url); - - // create empty map - DataMap dataMap = helper.getDataMap(); - - // process enitities ... throw out prototypes ... for now - List modelNames = new ArrayList(helper.modelNamesAsList()); - CollectionUtils.filter(modelNames, PredicateUtils.notPredicate(prototypeChecker)); - - Iterator it = modelNames.iterator(); - while (it.hasNext()) { - String name = (String) it.next(); - - // create and register entity - makeEntity(helper, name, generateClientClass); - } - - // now sort following inheritance hierarchy - Collections.sort(modelNames, new InheritanceComparator(dataMap)); - - // after all entities are loaded, process attributes - it = modelNames.iterator(); - while (it.hasNext()) { - String name = (String) it.next(); - - EOObjEntity e = (EOObjEntity) dataMap.getObjEntity(name); - // process entity attributes - makeAttributes(helper, e); - } - - // after all entities are loaded, process relationships - it = modelNames.iterator(); - while (it.hasNext()) { - String name = (String) it.next(); - makeRelationships(helper, dataMap.getObjEntity(name)); - } - - // after all normal relationships are loaded, process flattened - // relationships - it = modelNames.iterator(); - while (it.hasNext()) { - String name = (String) it.next(); - makeFlatRelationships(helper, dataMap.getObjEntity(name)); - } - - // now create missing reverse DB (but not OBJ) relationships - // since Cayenne requires them - it = modelNames.iterator(); - while (it.hasNext()) { - String name = (String) it.next(); - DbEntity dbEntity = dataMap.getObjEntity(name).getDbEntity(); - - if (dbEntity != null) { - makeReverseDbRelationships(dbEntity); - } - } - - // build SelectQueries out of EOFetchSpecifications... - it = modelNames.iterator(); - while (it.hasNext()) { - String name = (String) it.next(); - Iterator queries = helper.queryNames(name); - while (queries.hasNext()) { - String queryName = (String) queries.next(); - EOObjEntity entity = (EOObjEntity) dataMap.getObjEntity(name); - makeQuery(helper, entity, queryName); - } - } - - return dataMap; - } - - /** - * Returns whether an Entity is an EOF EOPrototypes entity. According to EOF - * conventions EOPrototypes and EO[Adapter]Prototypes entities are - * considered to be prototypes. - * - * @since 1.1 - */ - protected boolean isPrototypesEntity(String entityName) { - return prototypeChecker.evaluate(entityName); - } - - /** - * Creates an returns new EOModelHelper to process EOModel. Exists mostly - * for the benefit of subclasses. - */ - protected EOModelHelper makeHelper(URL url) throws Exception { - return new EOModelHelper(url); - } - - /** - * Creates a Cayenne query out of EOFetchSpecification data. - * - * @since 1.1 - */ - protected Query makeQuery(EOModelHelper helper, EOObjEntity entity, String queryName) { - - DataMap dataMap = helper.getDataMap(); - Map queryPlist = helper.queryPListMap(entity.getName(), queryName); - if (queryPlist == null) { - return null; - } - - AbstractQuery query; - if (queryPlist.containsKey("hints")) { // just a predefined SQL query - query = new EOSQLQuery(entity, queryPlist); - } else { - query = new EOQuery(entity, queryPlist); - } - query.setName(entity.qualifiedQueryName(queryName)); - dataMap.addQuery(query); - - return query; - } - - /** - * Creates and returns a new ObjEntity linked to a corresponding DbEntity. - */ - protected EOObjEntity makeEntity(EOModelHelper helper, String name, boolean generateClientClass) { - - DataMap dataMap = helper.getDataMap(); - Map entityPlist = helper.entityPListMap(name); - - // create ObjEntity - EOObjEntity objEntity = new EOObjEntity(name); - objEntity.setEoMap(entityPlist); - objEntity.setServerOnly(!generateClientClass); - String parent = (String) entityPlist.get("parent"); - objEntity.setClassName(helper.entityClass(name, generateClientClass)); - - if (parent != null) { - objEntity.setSubclass(true); - objEntity.setSuperClassName(helper.entityClass(parent, generateClientClass)); - } - - // add flag whether this entity is set as abstract in the model - objEntity.setAbstractEntity("Y".equals(entityPlist.get("isAbstractEntity"))); - - // create DbEntity...since EOF allows the same table to be - // associated with multiple EOEntities, check for name duplicates - String dbEntityName = (String) entityPlist.get("externalName"); - if (dbEntityName != null) { - - // ... if inheritance is involved and parent hierarchy uses the same - // DBEntity, - // do not create a DbEntity... - boolean createDbEntity = true; - if (parent != null) { - String parentName = parent; - while (parentName != null) { - Map parentData = helper.entityPListMap(parentName); - if (parentData == null) { - break; - } - - String parentExternalName = (String) parentData.get("externalName"); - if (parentExternalName == null) { - parentName = (String) parentData.get("parent"); - continue; - } - - if (dbEntityName.equals(parentExternalName)) { - createDbEntity = false; - } - - break; - } - } - - if (createDbEntity) { - int i = 0; - String dbEntityBaseName = dbEntityName; - while (dataMap.getDbEntity(dbEntityName) != null) { - dbEntityName = dbEntityBaseName + i++; - } - - objEntity.setDbEntityName(dbEntityName); - DbEntity de = new DbEntity(dbEntityName); - dataMap.addDbEntity(de); - } - } - - // set various flags - objEntity.setReadOnly("Y".equals(entityPlist.get("isReadOnly"))); - objEntity.setSuperEntityName((String) entityPlist.get("parent")); - - dataMap.addObjEntity(objEntity); - - return objEntity; - } - - /** - * Create ObjAttributes of the specified entity, as well as DbAttributes of - * the corresponding DbEntity. - */ - protected void makeAttributes(EOModelHelper helper, EOObjEntity objEntity) { - Map entityPlistMap = helper.entityPListMap(objEntity.getName()); - List primaryKeys = (List) entityPlistMap.get("primaryKeyAttributes"); - - List classProperties; - if (objEntity.isServerOnly()) { - classProperties = (List) entityPlistMap.get("classProperties"); - } else { - classProperties = (List) entityPlistMap.get("clientClassProperties"); - } - - List attributes = (List) entityPlistMap.get("attributes"); - DbEntity dbEntity = objEntity.getDbEntity(); - - if (primaryKeys == null) { - primaryKeys = Collections.EMPTY_LIST; - } - - if (classProperties == null) { - classProperties = Collections.EMPTY_LIST; - } - - if (attributes == null) { - attributes = Collections.EMPTY_LIST; - } - - // detect single table inheritance - boolean singleTableInheritance = false; - String parentName = (String) entityPlistMap.get("parent"); - while (parentName != null) { - Map parentData = helper.entityPListMap(parentName); - if (parentData == null) { - break; - } - - String parentExternalName = (String) parentData.get("externalName"); - if (parentExternalName == null) { - parentName = (String) parentData.get("parent"); - continue; - } - - if (dbEntity.getName() != null && dbEntity.getName().equals(parentExternalName)) { - singleTableInheritance = true; - } - - break; - } - - Iterator it = attributes.iterator(); - while (it.hasNext()) { - Map attrMap = (Map) it.next(); - - String prototypeName = (String) attrMap.get("prototypeName"); - Map prototypeAttrMap = helper.getPrototypeAttributeMapFor(prototypeName); - - String dbAttrName = (String) attrMap.get("columnName"); - if (null == dbAttrName) { - dbAttrName = (String) prototypeAttrMap.get("columnName"); - } - - String attrName = (String) attrMap.get("name"); - if (null == attrName) { - attrName = (String) prototypeAttrMap.get("name"); - } - - String attrType = (String) attrMap.get("valueClassName"); - if (null == attrType) { - attrType = (String) prototypeAttrMap.get("valueClassName"); - } - - String valueType = (String) attrMap.get("valueType"); - if (valueType == null) { - valueType = (String) prototypeAttrMap.get("valueType"); - } - - String javaType = helper.javaTypeForEOModelerType(attrType, valueType); - EODbAttribute dbAttr = null; - - if (dbAttrName != null && dbEntity != null) { - - // if inherited attribute, skip it for DbEntity... - if (!singleTableInheritance || dbEntity.getAttribute(dbAttrName) == null) { - - // create DbAttribute...since EOF allows the same column - // name for - // more than one Java attribute, we need to check for name - // duplicates - int i = 0; - String dbAttributeBaseName = dbAttrName; - while (dbEntity.getAttribute(dbAttrName) != null) { - dbAttrName = dbAttributeBaseName + i++; - } - - dbAttr = new EODbAttribute(dbAttrName, TypesMapping.getSqlTypeByJava(javaType), dbEntity); - dbAttr.setEoAttributeName(attrName); - dbEntity.addAttribute(dbAttr); - - int width = getInt("width", attrMap, prototypeAttrMap, -1); - if (width >= 0) { - dbAttr.setMaxLength(width); - } - - int scale = getInt("scale", attrMap, prototypeAttrMap, -1); - if (scale >= 0) { - dbAttr.setScale(scale); - } - - if (primaryKeys.contains(attrName)) - dbAttr.setPrimaryKey(true); - - Object allowsNull = attrMap.get("allowsNull"); - // TODO: Unclear that allowsNull should be inherited from - // EOPrototypes - // if (null == allowsNull) allowsNull = - // prototypeAttrMap.get("allowsNull");; - - dbAttr.setMandatory(!"Y".equals(allowsNull)); - } - } - - if (classProperties.contains(attrName)) { - EOObjAttribute attr = new EOObjAttribute(attrName, javaType, objEntity); - - // set readOnly flag of Attribute if either attribute is read or - // if entity is readOnly - String entityReadOnlyString = (String) entityPlistMap.get("isReadOnly"); - String attributeReadOnlyString = (String) attrMap.get("isReadOnly"); - if ("Y".equals(entityReadOnlyString) || "Y".equals(attributeReadOnlyString)) { - attr.setReadOnly(true); - } - - // set name instead of the actual attribute, as it may be - // inherited.... - attr.setDbAttributePath(dbAttrName); - objEntity.addAttribute(attr); - } - } - } - - int getInt(String key, Map map, Map prototypes, int defaultValue) { - - Object value = map.get(key); - if (value == null) { - value = prototypes.get(key); - } - - if (value == null) { - return defaultValue; - } - - // per CAY-752, value can be a String or a Number, so handle both - if (value instanceof Number) { - return ((Number) value).intValue(); - } else { - try { - return Integer.parseInt(value.toString()); - } catch (NumberFormatException nfex) { - return defaultValue; - } - } - } - - /** - * Create ObjRelationships of the specified entity, as well as - * DbRelationships of the corresponding DbEntity. - */ - protected void makeRelationships(EOModelHelper helper, ObjEntity objEntity) { - Map entityPlistMap = helper.entityPListMap(objEntity.getName()); - List classProps = (List) entityPlistMap.get("classProperties"); - List rinfo = (List) entityPlistMap.get("relationships"); - - Collection attributes = (Collection) entityPlistMap.get("attributes"); - - if (rinfo == null) { - return; - } - - if (classProps == null) { - classProps = Collections.EMPTY_LIST; - } - - if (attributes == null) { - attributes = Collections.EMPTY_LIST; - } - - DbEntity dbSrc = objEntity.getDbEntity(); - Iterator it = rinfo.iterator(); - while (it.hasNext()) { - Map relMap = (Map) it.next(); - String targetName = (String) relMap.get("destination"); - - // ignore flattened relationships for now - if (targetName == null) { - continue; - } - - String relName = (String) relMap.get("name"); - boolean toMany = "Y".equals(relMap.get("isToMany")); - boolean toDependentPK = "Y".equals(relMap.get("propagatesPrimaryKey")); - ObjEntity target = helper.getDataMap().getObjEntity(targetName); - - // target maybe null for cross-EOModel relationships - // ignoring those now. - if (target == null) { - continue; - } - - DbEntity dbTarget = target.getDbEntity(); - Map targetPlistMap = helper.entityPListMap(targetName); - Collection targetAttributes = (Collection) targetPlistMap.get("attributes"); - DbRelationship dbRel = null; - - // process underlying DbRelationship - // Note: there is no flattened rel. support here.... - // Note: source maybe null, e.g. an abstract entity. - if (dbSrc != null && dbTarget != null) { - - // in case of inheritance EOF stores duplicates of all inherited - // relationships, so we must skip this relationship in DB entity - // if it is - // already there... - - dbRel = dbSrc.getRelationship(relName); - if (dbRel == null) { - - dbRel = new DbRelationship(); - dbRel.setSourceEntity(dbSrc); - dbRel.setTargetEntityName(dbTarget); - dbRel.setToMany(toMany); - dbRel.setName(relName); - dbRel.setToDependentPK(toDependentPK); - dbSrc.addRelationship(dbRel); - - List joins = (List) relMap.get("joins"); - Iterator jIt = joins.iterator(); - while (jIt.hasNext()) { - Map joinMap = (Map) jIt.next(); - - DbJoin join = new DbJoin(dbRel); - - // find source attribute dictionary and extract the - // column name - String sourceAttributeName = (String) joinMap.get("sourceAttribute"); - join.setSourceName(columnName(attributes, sourceAttributeName)); - - String targetAttributeName = (String) joinMap.get("destinationAttribute"); - - join.setTargetName(columnName(targetAttributes, targetAttributeName)); - dbRel.addJoin(join); - } - } - } - - // only create obj relationship if it is a class property - if (classProps.contains(relName)) { - ObjRelationship rel = new ObjRelationship(); - rel.setName(relName); - rel.setSourceEntity(objEntity); - rel.setTargetEntityName(target); - objEntity.addRelationship(rel); - - if (dbRel != null) { - rel.addDbRelationship(dbRel); - } - } - } - } - - /** - * Create reverse DbRelationships that were not created so far, since - * Cayenne requires them. - * - * @since 1.0.5 - */ - protected void makeReverseDbRelationships(DbEntity dbEntity) { - if (dbEntity == null) { - throw new NullPointerException("Attempt to create reverse relationships for the null DbEntity."); - } - - // iterate over a copy of the collection, since in case of - // reflexive relationships, we may modify source entity relationship map - - for (DbRelationship relationship : new ArrayList<DbRelationship>(dbEntity.getRelationships())) { - - if (relationship.getReverseRelationship() == null) { - DbRelationship reverse = relationship.createReverseRelationship(); - - String name = DefaultUniqueNameGenerator.generate(NameCheckers.dbRelationship, reverse.getSourceEntity(), relationship.getName() + "Reverse"); - reverse.setName(name); - relationship.getTargetEntity().addRelationship(reverse); - } - } - } - - /** - * Create Flattened ObjRelationships of the specified entity. - */ - protected void makeFlatRelationships(EOModelHelper helper, ObjEntity e) { - Map info = helper.entityPListMap(e.getName()); - List rinfo = (List) info.get("relationships"); - if (rinfo == null) { - return; - } - - Iterator it = rinfo.iterator(); - while (it.hasNext()) { - Map relMap = (Map) it.next(); - String targetPath = (String) relMap.get("definition"); - - // ignore normal relationships - if (targetPath == null) { - continue; - } - - ObjRelationship flatRel = new ObjRelationship(); - flatRel.setName((String) relMap.get("name")); - flatRel.setSourceEntity(e); - - try { - flatRel.setDbRelationshipPath(targetPath); - } catch (ExpressionException ex) { - logger.warn("Invalid relationship: " + targetPath); - continue; - } - - // find target entity - Map entityInfo = info; - StringTokenizer toks = new StringTokenizer(targetPath, "."); - while (toks.hasMoreTokens() && entityInfo != null) { - String pathComponent = toks.nextToken(); - - // get relationship info and reset entityInfo, so that we could - // use - // entityInfo state as an indicator of valid flat relationship - // enpoint - // outside the loop - Collection relationshipInfo = (Collection) entityInfo.get("relationships"); - entityInfo = null; - - if (relationshipInfo == null) { - break; - } - - Iterator rit = relationshipInfo.iterator(); - while (rit.hasNext()) { - Map pathRelationship = (Map) rit.next(); - if (pathComponent.equals(pathRelationship.get("name"))) { - String targetName = (String) pathRelationship.get("destination"); - entityInfo = helper.entityPListMap(targetName); - break; - } - } - } - - if (entityInfo != null) { - flatRel.setTargetEntityName((String) entityInfo.get("name")); - } - - e.addRelationship(flatRel); - } - } - - /** - * Locates an attribute map matching the name and returns column name for - * this attribute. - * - * @since 1.1 - */ - String columnName(Collection entityAttributes, String attributeName) { - if (attributeName == null) { - return null; - } - - Iterator it = entityAttributes.iterator(); - while (it.hasNext()) { - Map map = (Map) it.next(); - if (attributeName.equals(map.get("name"))) { - return (String) map.get("columnName"); - } - } - - return null; - } - - // sorts ObjEntities so that subentities in inheritance hierarchy are shown - // last - final class InheritanceComparator implements Comparator { - - DataMap dataMap; - - InheritanceComparator(DataMap dataMap) { - this.dataMap = dataMap; - } - - public int compare(Object o1, Object o2) { - if (o1 == null) { - return o2 != null ? -1 : 0; - } else if (o2 == null) { - return 1; - } - - String name1 = o1.toString(); - String name2 = o2.toString(); - - ObjEntity e1 = dataMap.getObjEntity(name1); - ObjEntity e2 = dataMap.getObjEntity(name2); - - return compareEntities(e1, e2); - } - - int compareEntities(ObjEntity e1, ObjEntity e2) { - if (e1 == null) { - return e2 != null ? -1 : 0; - } else if (e2 == null) { - return 1; - } - - // entity goes first if it is a direct or indirect superentity of - // another - // one - if (e1.isSubentityOf(e2)) { - return 1; - } - - if (e2.isSubentityOf(e1)) { - return -1; - } - - // sort alphabetically - return e1.getName().compareTo(e2.getName()); - } - } + private static final Log logger = LogFactory.getLog(EOModelProcessor.class); + + protected Predicate prototypeChecker; + + public EOModelProcessor() { + prototypeChecker = new Predicate() { + + public boolean evaluate(Object object) { + if (object == null) { + return false; + } + + String entityName = object.toString(); + return entityName.startsWith("EO") && entityName.endsWith("Prototypes"); + } + }; + } + + /** + * @deprecated since 4.0 in favor of {@link #loadModeIndex(URL)}. + */ + @Deprecated + public Map loadModeIndex(String path) throws Exception { + return loadModeIndex(new File(path).toURI().toURL()); + } + + /** + * Returns index.eomodeld contents as a Map. + * + * @since 4.0 + */ + // TODO: refactor EOModelHelper to provide a similar method without loading + // all entity files in memory... here we simply copied stuff from + // EOModelHelper + public Map loadModeIndex(URL url) throws Exception { + + String urlString = url.toExternalForm(); + + if (!urlString.endsWith(".eomodeld")) { + url = new URL(urlString + ".eomodeld"); + } + + Parser plistParser = new Parser(); + + try (InputStream in = new URL(url, "index.eomodeld").openStream();) { + plistParser.ReInit(in); + return (Map) plistParser.propertyList(); + } + } + + /** + * @deprecated since 4.0 in favor of {@link #loadEOModel(URL)}. + */ + @Deprecated + public DataMap loadEOModel(String path) throws Exception { + return loadEOModel(path, false); + } + + /** + * @deprecated since 4.0 in favor of {@link #loadEOModel(URL, boolean)}. + */ + @Deprecated + public DataMap loadEOModel(String path, boolean generateClientClass) throws Exception { + return loadEOModel(new File(path).toURI().toURL(), generateClientClass); + } + + /** + * Performs EOModel loading. + * + * @param url + * URL of ".eomodeld" directory. + */ + public DataMap loadEOModel(URL url) throws Exception { + return loadEOModel(url, false); + } + + /** + * Performs EOModel loading. + * + * @param url + * URL of ".eomodeld" directory. + * @param generateClientClass + * if true then loading of EOModel is java client classes aware + * and the following processing will work with Java client class + * settings of the EOModel. + */ + public DataMap loadEOModel(URL url, boolean generateClientClass) throws Exception { + EOModelHelper helper = makeHelper(url); + + // create empty map + DataMap dataMap = helper.getDataMap(); + + // process enitities ... throw out prototypes ... for now + List modelNames = new ArrayList(helper.modelNamesAsList()); + CollectionUtils.filter(modelNames, PredicateUtils.notPredicate(prototypeChecker)); + + Iterator it = modelNames.iterator(); + while (it.hasNext()) { + String name = (String) it.next(); + + // create and register entity + makeEntity(helper, name, generateClientClass); + } + + // now sort following inheritance hierarchy + Collections.sort(modelNames, new InheritanceComparator(dataMap)); + + // after all entities are loaded, process attributes + it = modelNames.iterator(); + while (it.hasNext()) { + String name = (String) it.next(); + + EOObjEntity e = (EOObjEntity) dataMap.getObjEntity(name); + // process entity attributes + makeAttributes(helper, e); + } + + // after all entities are loaded, process relationships + it = modelNames.iterator(); + while (it.hasNext()) { + String name = (String) it.next(); + makeRelationships(helper, dataMap.getObjEntity(name)); + } + + // after all normal relationships are loaded, process flattened + // relationships + it = modelNames.iterator(); + while (it.hasNext()) { + String name = (String) it.next(); + makeFlatRelationships(helper, dataMap.getObjEntity(name)); + } + + // now create missing reverse DB (but not OBJ) relationships + // since Cayenne requires them + it = modelNames.iterator(); + while (it.hasNext()) { + String name = (String) it.next(); + DbEntity dbEntity = dataMap.getObjEntity(name).getDbEntity(); + + if (dbEntity != null) { + makeReverseDbRelationships(dbEntity); + } + } + + // build SelectQueries out of EOFetchSpecifications... + it = modelNames.iterator(); + while (it.hasNext()) { + String name = (String) it.next(); + Iterator queries = helper.queryNames(name); + while (queries.hasNext()) { + String queryName = (String) queries.next(); + EOObjEntity entity = (EOObjEntity) dataMap.getObjEntity(name); + makeQuery(helper, entity, queryName); + } + } + + return dataMap; + } + + /** + * Returns whether an Entity is an EOF EOPrototypes entity. According to EOF + * conventions EOPrototypes and EO[Adapter]Prototypes entities are + * considered to be prototypes. + * + * @since 1.1 + */ + protected boolean isPrototypesEntity(String entityName) { + return prototypeChecker.evaluate(entityName); + } + + /** + * Creates an returns new EOModelHelper to process EOModel. Exists mostly + * for the benefit of subclasses. + */ + protected EOModelHelper makeHelper(URL url) throws Exception { + return new EOModelHelper(url); + } + + /** + * Creates a Cayenne query out of EOFetchSpecification data. + * + * @since 1.1 + */ + protected Query makeQuery(EOModelHelper helper, EOObjEntity entity, String queryName) { + + DataMap dataMap = helper.getDataMap(); + Map queryPlist = helper.queryPListMap(entity.getName(), queryName); + if (queryPlist == null) { + return null; + } + + AbstractQuery query; + if (queryPlist.containsKey("hints")) { // just a predefined SQL query + query = new EOSQLQuery(entity, queryPlist); + } else { + query = new EOQuery(entity, queryPlist); + } + query.setName(entity.qualifiedQueryName(queryName)); + dataMap.addQuery(query); + + return query; + } + + /** + * Creates and returns a new ObjEntity linked to a corresponding DbEntity. + */ + protected EOObjEntity makeEntity(EOModelHelper helper, String name, boolean generateClientClass) { + + DataMap dataMap = helper.getDataMap(); + Map entityPlist = helper.entityPListMap(name); + + // create ObjEntity + EOObjEntity objEntity = new EOObjEntity(name); + objEntity.setEoMap(entityPlist); + objEntity.setServerOnly(!generateClientClass); + String parent = (String) entityPlist.get("parent"); + objEntity.setClassName(helper.entityClass(name, generateClientClass)); + + if (parent != null) { + objEntity.setSubclass(true); + objEntity.setSuperClassName(helper.entityClass(parent, generateClientClass)); + } + + // add flag whether this entity is set as abstract in the model + objEntity.setAbstractEntity("Y".equals(entityPlist.get("isAbstractEntity"))); + + // create DbEntity...since EOF allows the same table to be + // associated with multiple EOEntities, check for name duplicates + String dbEntityName = (String) entityPlist.get("externalName"); + if (dbEntityName != null) { + + // ... if inheritance is involved and parent hierarchy uses the same + // DBEntity, + // do not create a DbEntity... + boolean createDbEntity = true; + if (parent != null) { + String parentName = parent; + while (parentName != null) { + Map parentData = helper.entityPListMap(parentName); + if (parentData == null) { + break; + } + + String parentExternalName = (String) parentData.get("externalName"); + if (parentExternalName == null) { + parentName = (String) parentData.get("parent"); + continue; + } + + if (dbEntityName.equals(parentExternalName)) { + createDbEntity = false; + } + + break; + } + } + + if (createDbEntity) { + int i = 0; + String dbEntityBaseName = dbEntityName; + while (dataMap.getDbEntity(dbEntityName) != null) { + dbEntityName = dbEntityBaseName + i++; + } + + objEntity.setDbEntityName(dbEntityName); + DbEntity de = new DbEntity(dbEntityName); + dataMap.addDbEntity(de); + } + } + + // set various flags + objEntity.setReadOnly("Y".equals(entityPlist.get("isReadOnly"))); + objEntity.setSuperEntityName((String) entityPlist.get("parent")); + + dataMap.addObjEntity(objEntity); + + return objEntity; + } + + /** + * Create ObjAttributes of the specified entity, as well as DbAttributes of + * the corresponding DbEntity. + */ + protected void makeAttributes(EOModelHelper helper, EOObjEntity objEntity) { + Map entityPlistMap = helper.entityPListMap(objEntity.getName()); + List primaryKeys = (List) entityPlistMap.get("primaryKeyAttributes"); + + List classProperties; + if (objEntity.isServerOnly()) { + classProperties = (List) entityPlistMap.get("classProperties"); + } else { + classProperties = (List) entityPlistMap.get("clientClassProperties"); + } + + List attributes = (List) entityPlistMap.get("attributes"); + DbEntity dbEntity = objEntity.getDbEntity(); + + if (primaryKeys == null) { + primaryKeys = Collections.EMPTY_LIST; + } + + if (classProperties == null) { + classProperties = Collections.EMPTY_LIST; + } + + if (attributes == null) { + attributes = Collections.EMPTY_LIST; + } + + // detect single table inheritance + boolean singleTableInheritance = false; + String parentName = (String) entityPlistMap.get("parent"); + while (parentName != null) { + Map parentData = helper.entityPListMap(parentName); + if (parentData == null) { + break; + } + + String parentExternalName = (String) parentData.get("externalName"); + if (parentExternalName == null) { + parentName = (String) parentData.get("parent"); + continue; + } + + if (dbEntity.getName() != null && dbEntity.getName().equals(parentExternalName)) { + singleTableInheritance = true; + } + + break; + } + + Iterator it = attributes.iterator(); + while (it.hasNext()) { + Map attrMap = (Map) it.next(); + + String prototypeName = (String) attrMap.get("prototypeName"); + Map prototypeAttrMap = helper.getPrototypeAttributeMapFor(prototypeName); + + String dbAttrName = (String) attrMap.get("columnName"); + if (null == dbAttrName) { + dbAttrName = (String) prototypeAttrMap.get("columnName"); + } + + String attrName = (String) attrMap.get("name"); + if (null == attrName) { + attrName = (String) prototypeAttrMap.get("name"); + } + + String attrType = (String) attrMap.get("valueClassName"); + if (null == attrType) { + attrType = (String) prototypeAttrMap.get("valueClassName"); + } + + String valueType = (String) attrMap.get("valueType"); + if (valueType == null) { + valueType = (String) prototypeAttrMap.get("valueType"); + } + + String javaType = helper.javaTypeForEOModelerType(attrType, valueType); + EODbAttribute dbAttr = null; + + if (dbAttrName != null && dbEntity != null) { + + // if inherited attribute, skip it for DbEntity... + if (!singleTableInheritance || dbEntity.getAttribute(dbAttrName) == null) { + + // create DbAttribute...since EOF allows the same column + // name for + // more than one Java attribute, we need to check for name + // duplicates + int i = 0; + String dbAttributeBaseName = dbAttrName; + while (dbEntity.getAttribute(dbAttrName) != null) { + dbAttrName = dbAttributeBaseName + i++; + } + + dbAttr = new EODbAttribute(dbAttrName, TypesMapping.getSqlTypeByJava(javaType), dbEntity); + dbAttr.setEoAttributeName(attrName); + dbEntity.addAttribute(dbAttr); + + int width = getInt("width", attrMap, prototypeAttrMap, -1); + if (width >= 0) { + dbAttr.setMaxLength(width); + } + + int scale = getInt("scale", attrMap, prototypeAttrMap, -1); + if (scale >= 0) { + dbAttr.setScale(scale); + } + + if (primaryKeys.contains(attrName)) + dbAttr.setPrimaryKey(true); + + Object allowsNull = attrMap.get("allowsNull"); + // TODO: Unclear that allowsNull should be inherited from + // EOPrototypes + // if (null == allowsNull) allowsNull = + // prototypeAttrMap.get("allowsNull");; + + dbAttr.setMandatory(!"Y".equals(allowsNull)); + } + } + + if (classProperties.contains(attrName)) { + EOObjAttribute attr = new EOObjAttribute(attrName, javaType, objEntity); + + // set readOnly flag of Attribute if either attribute is read or + // if entity is readOnly + String entityReadOnlyString = (String) entityPlistMap.get("isReadOnly"); + String attributeReadOnlyString = (String) attrMap.get("isReadOnly"); + if ("Y".equals(entityReadOnlyString) || "Y".equals(attributeReadOnlyString)) { + attr.setReadOnly(true); + } + + // set name instead of the actual attribute, as it may be + // inherited.... + attr.setDbAttributePath(dbAttrName); + objEntity.addAttribute(attr); + } + } + } + + int getInt(String key, Map map, Map prototypes, int defaultValue) { + + Object value = map.get(key); + if (value == null) { + value = prototypes.get(key); + } + + if (value == null) { + return defaultValue; + } + + // per CAY-752, value can be a String or a Number, so handle both + if (value instanceof Number) { + return ((Number) value).intValue(); + } else { + try { + return Integer.parseInt(value.toString()); + } catch (NumberFormatException nfex) { + return defaultValue; + } + } + } + + /** + * Create ObjRelationships of the specified entity, as well as + * DbRelationships of the corresponding DbEntity. + */ + protected void makeRelationships(EOModelHelper helper, ObjEntity objEntity) { + Map entityPlistMap = helper.entityPListMap(objEntity.getName()); + List classProps = (List) entityPlistMap.get("classProperties"); + List rinfo = (List) entityPlistMap.get("relationships"); + + Collection attributes = (Collection) entityPlistMap.get("attributes"); + + if (rinfo == null) { + return; + } + + if (classProps == null) { + classProps = Collections.EMPTY_LIST; + } + + if (attributes == null) { + attributes = Collections.EMPTY_LIST; + } + + DbEntity dbSrc = objEntity.getDbEntity(); + Iterator it = rinfo.iterator(); + while (it.hasNext()) { + Map relMap = (Map) it.next(); + String targetName = (String) relMap.get("destination"); + + // ignore flattened relationships for now + if (targetName == null) { + continue; + } + + String relName = (String) relMap.get("name"); + boolean toMany = "Y".equals(relMap.get("isToMany")); + boolean toDependentPK = "Y".equals(relMap.get("propagatesPrimaryKey")); + ObjEntity target = helper.getDataMap().getObjEntity(targetName); + + // target maybe null for cross-EOModel relationships + // ignoring those now. + if (target == null) { + continue; + } + + DbEntity dbTarget = target.getDbEntity(); + Map targetPlistMap = helper.entityPListMap(targetName); + Collection targetAttributes = (Collection) targetPlistMap.get("attributes"); + DbRelationship dbRel = null; + + // process underlying DbRelationship + // Note: there is no flattened rel. support here.... + // Note: source maybe null, e.g. an abstract entity. + if (dbSrc != null && dbTarget != null) { + + // in case of inheritance EOF stores duplicates of all inherited + // relationships, so we must skip this relationship in DB entity + // if it is + // already there... + + dbRel = dbSrc.getRelationship(relName); + if (dbRel == null) { + + dbRel = new DbRelationship(); + dbRel.setSourceEntity(dbSrc); + dbRel.setTargetEntityName(dbTarget); + dbRel.setToMany(toMany); + dbRel.setName(relName); + dbRel.setToDependentPK(toDependentPK); + dbSrc.addRelationship(dbRel); + + List joins = (List) relMap.get("joins"); + Iterator jIt = joins.iterator(); + while (jIt.hasNext()) { + Map joinMap = (Map) jIt.next(); + + DbJoin join = new DbJoin(dbRel); + + // find source attribute dictionary and extract the + // column name + String sourceAttributeName = (String) joinMap.get("sourceAttribute"); + join.setSourceName(columnName(attributes, sourceAttributeName)); + + String targetAttributeName = (String) joinMap.get("destinationAttribute"); + + join.setTargetName(columnName(targetAttributes, targetAttributeName)); + dbRel.addJoin(join); + } + } + } + + // only create obj relationship if it is a class property + if (classProps.contains(relName)) { + ObjRelationship rel = new ObjRelationship(); + rel.setName(relName); + rel.setSourceEntity(objEntity); + rel.setTargetEntityName(target); + objEntity.addRelationship(rel); + + if (dbRel != null) { + rel.addDbRelationship(dbRel); + } + } + } + } + + /** + * Create reverse DbRelationships that were not created so far, since + * Cayenne requires them. + * + * @since 1.0.5 + */ + protected void makeReverseDbRelationships(DbEntity dbEntity) { + if (dbEntity == null) { + throw new NullPointerException("Attempt to create reverse relationships for the null DbEntity."); + } + + // iterate over a copy of the collection, since in case of + // reflexive relationships, we may modify source entity relationship map + + for (DbRelationship relationship : new ArrayList<DbRelationship>(dbEntity.getRelationships())) { + + if (relationship.getReverseRelationship() == null) { + DbRelationship reverse = relationship.createReverseRelationship(); + + String name = DefaultUniqueNameGenerator.generate(NameCheckers.dbRelationship, + reverse.getSourceEntity(), relationship.getName() + "Reverse"); + reverse.setName(name); + relationship.getTargetEntity().addRelationship(reverse); + } + } + } + + /** + * Create Flattened ObjRelationships of the specified entity. + */ + protected void makeFlatRelationships(EOModelHelper helper, ObjEntity e) { + Map info = helper.entityPListMap(e.getName()); + List rinfo = (List) info.get("relationships"); + if (rinfo == null) { + return; + } + + Iterator it = rinfo.iterator(); + while (it.hasNext()) { + Map relMap = (Map) it.next(); + String targetPath = (String) relMap.get("definition"); + + // ignore normal relationships + if (targetPath == null) { + continue; + } + + ObjRelationship flatRel = new ObjRelationship(); + flatRel.setName((String) relMap.get("name")); + flatRel.setSourceEntity(e); + + try { + flatRel.setDbRelationshipPath(targetPath); + } catch (ExpressionException ex) { + logger.warn("Invalid relationship: " + targetPath); + continue; + } + + // find target entity + Map entityInfo = info; + StringTokenizer toks = new StringTokenizer(targetPath, "."); + while (toks.hasMoreTokens() && entityInfo != null) { + String pathComponent = toks.nextToken(); + + // get relationship info and reset entityInfo, so that we could + // use + // entityInfo state as an indicator of valid flat relationship + // enpoint + // outside the loop + Collection relationshipInfo = (Collection) entityInfo.get("relationships"); + entityInfo = null; + + if (relationshipInfo == null) { + break; + } + + Iterator rit = relationshipInfo.iterator(); + while (rit.hasNext()) { + Map pathRelationship = (Map) rit.next(); + if (pathComponent.equals(pathRelationship.get("name"))) { + String targetName = (String) pathRelationship.get("destination"); + entityInfo = helper.entityPListMap(targetName); + break; + } + } + } + + if (entityInfo != null) { + flatRel.setTargetEntityName((String) entityInfo.get("name")); + } + + e.addRelationship(flatRel); + } + } + + /** + * Locates an attribute map matching the name and returns column name for + * this attribute. + * + * @since 1.1 + */ + String columnName(Collection entityAttributes, String attributeName) { + if (attributeName == null) { + return null; + } + + Iterator it = entityAttributes.iterator(); + while (it.hasNext()) { + Map map = (Map) it.next(); + if (attributeName.equals(map.get("name"))) { + return (String) map.get("columnName"); + } + } + + return null; + } + + // sorts ObjEntities so that subentities in inheritance hierarchy are shown + // last + final class InheritanceComparator implements Comparator { + + DataMap dataMap; + + InheritanceComparator(DataMap dataMap) { + this.dataMap = dataMap; + } + + public int compare(Object o1, Object o2) { + if (o1 == null) { + return o2 != null ? -1 : 0; + } else if (o2 == null) { + return 1; + } + + String name1 = o1.toString(); + String name2 = o2.toString(); + + ObjEntity e1 = dataMap.getObjEntity(name1); + ObjEntity e2 = dataMap.getObjEntity(name2); + + return compareEntities(e1, e2); + } + + int compareEntities(ObjEntity e1, ObjEntity e2) { + if (e1 == null) { + return e2 != null ? -1 : 0; + } else if (e2 == null) { + return 1; + } + + // entity goes first if it is a direct or indirect superentity of + // another + // one + if (e1.isSubentityOf(e2)) { + return 1; + } + + if (e2.isSubentityOf(e1)) { + return -1; + } + + // sort alphabetically + return e1.getName().compareTo(e2.getName()); + } + } }
http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/PropertyListSerialization.java ---------------------------------------------------------------------- diff --git a/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/PropertyListSerialization.java b/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/PropertyListSerialization.java index e58e62d..7393f88 100644 --- a/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/PropertyListSerialization.java +++ b/modeler/cayenne-wocompat/src/main/java/org/apache/cayenne/wocompat/PropertyListSerialization.java @@ -36,247 +36,229 @@ import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.wocompat.parser.Parser; /** - * A <b>PropertyListSerialization</b> is a utility class that reads and stores files in - * NeXT/Apple property list format. Unlike corresponding WebObjects class, - * <code>PropertyListSerialization</code> uses standard Java collections (lists and - * maps) to store property lists. + * A <b>PropertyListSerialization</b> is a utility class that reads and stores + * files in NeXT/Apple property list format. Unlike corresponding WebObjects + * class, <code>PropertyListSerialization</code> uses standard Java collections + * (lists and maps) to store property lists. * */ public class PropertyListSerialization { - /** - * Reads a property list file. Returns a property list object, that is normally a - * java.util.List or a java.util.Map, but can also be a String or a Number. - */ - public static Object propertyListFromFile(File f) throws FileNotFoundException { - return propertyListFromFile(f, null); - } - - /** - * Reads a property list file. Returns a property list object, that is normally a - * java.util.List or a java.util.Map, but can also be a String or a Number. - */ - public static Object propertyListFromFile(File f, PlistDataStructureFactory factory) - throws FileNotFoundException { - if (!f.isFile()) { - throw new FileNotFoundException("No such file: " + f); - } - - return new Parser(f, factory).propertyList(); - } - - /** - * Reads a property list data from InputStream. Returns a property list o bject, that - * is normally a java.util.List or a java.util.Map, but can also be a String or a - * Number. - */ - public static Object propertyListFromStream(InputStream in) { - return propertyListFromStream(in, null); - } - - /** - * Reads a property list data from InputStream. Returns a property list o bject, that - * is normally a java.util.List or a java.util.Map, but can also be a String or a - * Number. - */ - public static Object propertyListFromStream( - InputStream in, - PlistDataStructureFactory factory) { - return new Parser(in, factory).propertyList(); - } - - /** - * Saves property list to file. - */ - public static void propertyListToFile(File f, Object plist) { - try { - BufferedWriter out = new BufferedWriter(new FileWriter(f)); - try { - writeObject("", out, plist); - } - finally { - out.close(); - } - } - catch (IOException ioex) { - throw new CayenneRuntimeException("Error saving plist.", ioex); - } - } - - /** - * Saves property list to file. - */ - public static void propertyListToStream(OutputStream os, Object plist) { - try { - BufferedWriter out = new BufferedWriter(new OutputStreamWriter(os)); - try { - writeObject("", out, plist); - } - finally { - out.close(); - } - } - catch (IOException ioex) { - throw new CayenneRuntimeException("Error saving plist.", ioex); - } - } - - /** - * Internal method to recursively write a property list object. - */ - protected static void writeObject(String offset, Writer out, Object plist) - throws IOException { - if (plist == null) { - return; - } - - if (plist instanceof Collection) { - Collection list = (Collection) plist; - - out.write('\n'); - out.write(offset); - - if (list.size() == 0) { - out.write("()"); - return; - } - - out.write("(\n"); - - String childOffset = offset + " "; - Iterator it = list.iterator(); - boolean appended = false; - while (it.hasNext()) { - // Java collections can contain nulls, skip them - Object obj = it.next(); - if (obj != null) { - if (appended) { - out.write(", \n"); - } - - out.write(childOffset); - writeObject(childOffset, out, obj); - appended = true; - } - } - - out.write('\n'); - out.write(offset); - out.write(')'); - } - else if (plist instanceof Map) { - Map map = (Map) plist; - out.write('\n'); - out.write(offset); - - if (map.size() == 0) { - out.write("{}"); - return; - } - - out.write("{"); - - String childOffset = offset + " "; - - Iterator it = map.entrySet().iterator(); - while (it.hasNext()) { - // Java collections can contain nulls, skip them - Map.Entry entry = (Map.Entry) it.next(); - Object key = entry.getKey(); - if (key == null) { - continue; - } - Object obj = entry.getValue(); - if (obj == null) { - continue; - } - out.write('\n'); - out.write(childOffset); - out.write(quoteString(key.toString())); - out.write(" = "); - writeObject(childOffset, out, obj); - out.write(';'); - } - - out.write('\n'); - out.write(offset); - out.write('}'); - } - else if (plist instanceof String) { - out.write(quoteString(plist.toString())); - } - else if (plist instanceof Number) { - out.write(plist.toString()); - } - else { - throw new CayenneRuntimeException( - "Unsupported class for property list serialization: " - + plist.getClass().getName()); - } - } - - /** - * Escapes all doublequotes and backslashes. - */ - protected static String escapeString(String str) { - char[] chars = str.toCharArray(); - int len = chars.length; - StringBuilder buf = new StringBuilder(len + 3); - - for (int i = 0; i < len; i++) { - if (chars[i] == '\"' || chars[i] == '\\') { - buf.append('\\'); - } - buf.append(chars[i]); - } - - return buf.toString(); - } - - /** - * Returns a quoted String, with all the escapes preprocessed. May return an unquoted - * String if it contains no special characters. The rule for a non-special character - * is the following: - * - * <pre> - * c >= 'a' && c <= 'z' - * c >= 'A' && c <= 'Z' - * c >= '0' && c <= '9' - * c == '_' - * c == '$' - * c == ':' - * c == '.' - * c == '/' - * </pre> - */ - protected static String quoteString(String str) { - boolean shouldQuote = false; - - // scan string for special chars, - // if we have them, string must be quoted - - String noQuoteExtras = "_$:./"; - char[] chars = str.toCharArray(); - int len = chars.length; - if (len == 0) { - shouldQuote = true; - } - for (int i = 0; !shouldQuote && i < len; i++) { - char c = chars[i]; - - if ((c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') - || noQuoteExtras.indexOf(c) >= 0) { - continue; - } - - shouldQuote = true; - } - - str = escapeString(str); - return (shouldQuote) ? '\"' + str + '\"' : str; - } - - + /** + * Reads a property list file. Returns a property list object, that is + * normally a java.util.List or a java.util.Map, but can also be a String or + * a Number. + */ + public static Object propertyListFromFile(File f) throws FileNotFoundException { + return propertyListFromFile(f, null); + } + + /** + * Reads a property list file. Returns a property list object, that is + * normally a java.util.List or a java.util.Map, but can also be a String or + * a Number. + */ + public static Object propertyListFromFile(File f, PlistDataStructureFactory factory) throws FileNotFoundException { + if (!f.isFile()) { + throw new FileNotFoundException("No such file: " + f); + } + + return new Parser(f, factory).propertyList(); + } + + /** + * Reads a property list data from InputStream. Returns a property list o + * bject, that is normally a java.util.List or a java.util.Map, but can also + * be a String or a Number. + */ + public static Object propertyListFromStream(InputStream in) { + return propertyListFromStream(in, null); + } + + /** + * Reads a property list data from InputStream. Returns a property list o + * bject, that is normally a java.util.List or a java.util.Map, but can also + * be a String or a Number. + */ + public static Object propertyListFromStream(InputStream in, PlistDataStructureFactory factory) { + return new Parser(in, factory).propertyList(); + } + + /** + * Saves property list to file. + */ + public static void propertyListToFile(File f, Object plist) { + try { + + try (BufferedWriter out = new BufferedWriter(new FileWriter(f));) { + writeObject("", out, plist); + } + } catch (IOException ioex) { + throw new CayenneRuntimeException("Error saving plist.", ioex); + } + } + + /** + * Saves property list to file. + */ + public static void propertyListToStream(OutputStream os, Object plist) { + try { + + try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(os));) { + writeObject("", out, plist); + } + } catch (IOException ioex) { + throw new CayenneRuntimeException("Error saving plist.", ioex); + } + } + + /** + * Internal method to recursively write a property list object. + */ + protected static void writeObject(String offset, Writer out, Object plist) throws IOException { + if (plist == null) { + return; + } + + if (plist instanceof Collection) { + Collection list = (Collection) plist; + + out.write('\n'); + out.write(offset); + + if (list.size() == 0) { + out.write("()"); + return; + } + + out.write("(\n"); + + String childOffset = offset + " "; + Iterator it = list.iterator(); + boolean appended = false; + while (it.hasNext()) { + // Java collections can contain nulls, skip them + Object obj = it.next(); + if (obj != null) { + if (appended) { + out.write(", \n"); + } + + out.write(childOffset); + writeObject(childOffset, out, obj); + appended = true; + } + } + + out.write('\n'); + out.write(offset); + out.write(')'); + } else if (plist instanceof Map) { + Map map = (Map) plist; + out.write('\n'); + out.write(offset); + + if (map.size() == 0) { + out.write("{}"); + return; + } + + out.write("{"); + + String childOffset = offset + " "; + + Iterator it = map.entrySet().iterator(); + while (it.hasNext()) { + // Java collections can contain nulls, skip them + Map.Entry entry = (Map.Entry) it.next(); + Object key = entry.getKey(); + if (key == null) { + continue; + } + Object obj = entry.getValue(); + if (obj == null) { + continue; + } + out.write('\n'); + out.write(childOffset); + out.write(quoteString(key.toString())); + out.write(" = "); + writeObject(childOffset, out, obj); + out.write(';'); + } + + out.write('\n'); + out.write(offset); + out.write('}'); + } else if (plist instanceof String) { + out.write(quoteString(plist.toString())); + } else if (plist instanceof Number) { + out.write(plist.toString()); + } else { + throw new CayenneRuntimeException("Unsupported class for property list serialization: " + + plist.getClass().getName()); + } + } + + /** + * Escapes all doublequotes and backslashes. + */ + protected static String escapeString(String str) { + char[] chars = str.toCharArray(); + int len = chars.length; + StringBuilder buf = new StringBuilder(len + 3); + + for (int i = 0; i < len; i++) { + if (chars[i] == '\"' || chars[i] == '\\') { + buf.append('\\'); + } + buf.append(chars[i]); + } + + return buf.toString(); + } + + /** + * Returns a quoted String, with all the escapes preprocessed. May return an + * unquoted String if it contains no special characters. The rule for a + * non-special character is the following: + * + * <pre> + * c >= 'a' && c <= 'z' + * c >= 'A' && c <= 'Z' + * c >= '0' && c <= '9' + * c == '_' + * c == '$' + * c == ':' + * c == '.' + * c == '/' + * </pre> + */ + protected static String quoteString(String str) { + boolean shouldQuote = false; + + // scan string for special chars, + // if we have them, string must be quoted + + String noQuoteExtras = "_$:./"; + char[] chars = str.toCharArray(); + int len = chars.length; + if (len == 0) { + shouldQuote = true; + } + for (int i = 0; !shouldQuote && i < len; i++) { + char c = chars[i]; + + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') + || noQuoteExtras.indexOf(c) >= 0) { + continue; + } + + shouldQuote = true; + } + + str = escapeString(str); + return (shouldQuote) ? '\"' + str + '\"' : str; + } + } http://git-wip-us.apache.org/repos/asf/cayenne/blob/26d8434d/modeler/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOModelHelperTest.java ---------------------------------------------------------------------- diff --git a/modeler/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOModelHelperTest.java b/modeler/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOModelHelperTest.java index 0d78ea6..f2b5201 100644 --- a/modeler/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOModelHelperTest.java +++ b/modeler/cayenne-wocompat/src/test/java/org/apache/cayenne/wocompat/EOModelHelperTest.java @@ -19,8 +19,12 @@ package org.apache.cayenne.wocompat; -import org.junit.Before; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.IOException; import java.io.InputStream; @@ -30,91 +34,86 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; public class EOModelHelperTest { - protected EOModelHelper helper; - - @Before - public void setUp() throws Exception { - URL url = getClass().getClassLoader().getResource("wotests/art.eomodeld/"); - assertNotNull(url); - helper = new EOModelHelper(url); - } - - @Test - public void testModelNames() throws Exception { - Iterator names = helper.modelNames(); - - // collect to list and then analyze - List list = new ArrayList(); - while (names.hasNext()) { - list.add(names.next()); - } - - assertEquals(8, list.size()); - assertTrue(list.contains("Artist")); - assertTrue(list.contains("Painting")); - assertTrue(list.contains("ExhibitType")); - } - - @Test - public void testQueryNames() throws Exception { - Iterator artistNames = helper.queryNames("Artist"); - assertFalse(artistNames.hasNext()); - - Iterator etNames = helper.queryNames("ExhibitType"); - assertTrue(etNames.hasNext()); - - // collect to list and then analyze - List list = new ArrayList(); - while (etNames.hasNext()) { - list.add(etNames.next()); - } - - assertEquals(2, list.size()); - assertTrue(list.contains("FetchAll")); - assertTrue(list.contains("TestQuery")); - } - - @Test - public void testQueryPListMap() throws Exception { - assertNull(helper.queryPListMap("Artist", "AAA")); - assertNull(helper.queryPListMap("ExhibitType", "AAA")); - - Map query = helper.queryPListMap("ExhibitType", "FetchAll"); - assertNotNull(query); - assertFalse(query.isEmpty()); - } - - @Test - public void testLoadQueryIndex() throws Exception { - Map index = helper.loadQueryIndex("ExhibitType"); - assertNotNull(index); - assertTrue(index.containsKey("FetchAll")); - } - - @Test - public void testOpenQueryStream() throws Exception { - InputStream in = helper.openQueryStream("ExhibitType"); - assertNotNull(in); - in.close(); - } - - @Test - public void testOpenNonExistentQueryStream() throws Exception { - try { - helper.openQueryStream("Artist"); - fail("Exception expected - artist has no fetch spec."); - } - catch (IOException ioex) { - // expected... - } - } + protected EOModelHelper helper; + + @Before + public void setUp() throws Exception { + URL url = getClass().getClassLoader().getResource("wotests/art.eomodeld/"); + assertNotNull(url); + helper = new EOModelHelper(url); + } + + @Test + public void testModelNames() throws Exception { + Iterator names = helper.modelNames(); + + // collect to list and then analyze + List list = new ArrayList(); + while (names.hasNext()) { + list.add(names.next()); + } + + assertEquals(8, list.size()); + assertTrue(list.contains("Artist")); + assertTrue(list.contains("Painting")); + assertTrue(list.contains("ExhibitType")); + } + + @Test + public void testQueryNames() throws Exception { + Iterator artistNames = helper.queryNames("Artist"); + assertFalse(artistNames.hasNext()); + + Iterator etNames = helper.queryNames("ExhibitType"); + assertTrue(etNames.hasNext()); + + // collect to list and then analyze + List list = new ArrayList(); + while (etNames.hasNext()) { + list.add(etNames.next()); + } + + assertEquals(2, list.size()); + assertTrue(list.contains("FetchAll")); + assertTrue(list.contains("TestQuery")); + } + + @Test + public void testQueryPListMap() throws Exception { + assertNull(helper.queryPListMap("Artist", "AAA")); + assertNull(helper.queryPListMap("ExhibitType", "AAA")); + + Map query = helper.queryPListMap("ExhibitType", "FetchAll"); + assertNotNull(query); + assertFalse(query.isEmpty()); + } + + @Test + public void testLoadQueryIndex() throws Exception { + Map index = helper.loadQueryIndex("ExhibitType"); + assertNotNull(index); + assertTrue(index.containsKey("FetchAll")); + } + + @Test + public void testOpenQueryStream() throws Exception { + try (InputStream in = helper.openQueryStream("ExhibitType");) { + assertNotNull(in); + } + } + + @Test + public void testOpenNonExistentQueryStream() throws Exception { + try { + helper.openQueryStream("Artist"); + fail("Exception expected - artist has no fetch spec."); + } catch (IOException ioex) { + // expected... + } + } }
