org.apache.cayenne.wocompat.EOModelHelper Maven / Gradle / Ivy
The newest version!
/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.apache.cayenne.wocompat;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.wocompat.parser.Parser;
/**
* Helper class used by EOModelProcessor. EOModelHelper loads an EOModel from
* the specified location and gives its users access to the untyped EOModel
* information.
*/
public class EOModelHelper {
private Parser plistParser = new Parser();
protected URL modelUrl;
protected Map entityIndex;
protected Map entityClassIndex;
protected Map entityQueryIndex;
protected Map entityClientClassIndex;
protected DataMap dataMap;
private Map prototypeValues;
public EOModelHelper(URL modelUrl) throws Exception {
this.modelUrl = modelUrl;
this.dataMap = new DataMap(findModelName(modelUrl.toExternalForm()));
// load index file
List modelIndex = (List) loadModelIndex().get("entities");
// load entity indices
entityIndex = new HashMap();
entityClassIndex = new HashMap();
entityClientClassIndex = new HashMap();
entityQueryIndex = new HashMap();
Iterator it = modelIndex.iterator();
while (it.hasNext()) {
Map info = (Map) it.next();
String name = (String) info.get("name");
entityIndex.put(name, loadEntityIndex(name));
entityQueryIndex.put(name, loadQueryIndex(name));
entityClassIndex.put(name, info.get("className"));
Map entityPlistMap = entityPListMap(name);
// get client class information
Map internalInfo = (Map) entityPlistMap.get("internalInfo");
if (internalInfo != null) {
String clientClassName = (String) internalInfo.get("_javaClientClassName");
entityClientClassIndex.put(name, clientClassName);
}
}
it = modelIndex.iterator();
while (it.hasNext()) {
Map info = (Map) it.next();
String name = (String) info.get("name");
Map entityPlistMap = entityPListMap(name);
List classProperties = (List) entityPlistMap.get("classProperties");
if (classProperties == null) {
classProperties = Collections.EMPTY_LIST;
}
// get client class information
Map internalInfo = (Map) entityPlistMap.get("internalInfo");
List clientClassProperties = (internalInfo != null) ? (List) internalInfo.get("_clientClassPropertyNames")
: null;
// guard against no internal info and no client class properties
if (clientClassProperties == null) {
clientClassProperties = Collections.EMPTY_LIST;
}
// there is a bug in EOModeler it sometimes keeps outdated
// properties in
// the client property list. This removes them
clientClassProperties.retainAll(classProperties);
// remove all properties from the entity properties that are already
// defined
// in
// a potential parent class.
String parentEntity = (String) entityPlistMap.get("parent");
while (parentEntity != null) {
Map parentEntityPListMap = entityPListMap(parentEntity);
List parentClassProps = (List) parentEntityPListMap.get("classProperties");
classProperties.removeAll(parentClassProps);
// get client class information of parent
Map parentInternalInfo = (Map) parentEntityPListMap.get("internalInfo");
if (parentInternalInfo != null) {
List parentClientClassProps = (List) parentInternalInfo.get("_clientClassPropertyNames");
clientClassProperties.removeAll(parentClientClassProps);
}
parentEntity = (String) parentEntityPListMap.get("parent");
}
// put back processed properties to the map
entityPlistMap.put("classProperties", classProperties);
// add client classes directly for easier access
entityPlistMap.put("clientClassProperties", clientClassProperties);
}
}
/**
* Performs Objective C data types conversion to Java types.
*
* @since 1.1
* @return String representation for Java type corresponding to String
* representation of Objective C type.
*/
public String javaTypeForEOModelerType(String valueClassName, String valueType) {
if (valueClassName == null) {
return null;
}
if (valueClassName.equals("NSString")) {
return String.class.getName();
}
if (valueClassName.equals("NSNumber")) {
Class numericClass = numericAttributeClass(valueType);
return (numericClass != null) ? numericClass.getName() : Number.class.getName();
}
if (valueClassName.equals("NSCalendarDate"))
return "java.sql.Timestamp";
if (valueClassName.equals("NSDecimalNumber")) {
Class numericClass = numericAttributeClass(valueType);
return (numericClass != null) ? numericClass.getName() : BigDecimal.class.getName();
}
if (valueClassName.equals("NSData"))
return "byte[]";
// don't know what the class is mapped to...
// do some minimum sanity check and use as is
try {
return Class.forName(valueClassName).getName();
} catch (ClassNotFoundException aClassNotFoundException) {
try {
return Class.forName("java.lang." + valueClassName).getName();
} catch (ClassNotFoundException anotherClassNotFoundException) {
try {
return Class.forName("java.util." + valueClassName).getName();
} catch (ClassNotFoundException yetAnotherClassNotFoundException) {
try {
return ClassLoader.getSystemClassLoader().loadClass(valueClassName).getName();
} catch (ClassNotFoundException e) {
// likely a custom class
return valueClassName;
}
}
}
}
}
/**
* @since 1.1
*/
// TODO: create a lookup map, maybe XML-loaded...
protected Class numericAttributeClass(String valueType) {
if (valueType == null) {
return null;
} else if ("b".equals(valueType)) {
return Byte.class;
} else if ("s".equals(valueType)) {
return Short.class;
} else if ("i".equals(valueType)) {
return Integer.class;
} else if ("l".equals(valueType)) {
return Long.class;
} else if ("f".equals(valueType)) {
return Float.class;
} else if ("d".equals(valueType)) {
return Double.class;
} else if ("B".equals(valueType)) {
return BigDecimal.class;
} else if ("c".equals(valueType)) {
return Boolean.class;
} else {
return null;
}
}
/** Returns a DataMap associated with this helper. */
public DataMap getDataMap() {
return dataMap;
}
/** Returns EOModel location as URL. */
public URL getModelUrl() {
return modelUrl;
}
/**
* Returns an iterator of model names.
*/
public Iterator modelNames() {
return entityClassIndex.keySet().iterator();
}
/**
* Returns a list of model entity names.
*
* @since 1.1
*/
public List modelNamesAsList() {
return new ArrayList<>(entityClassIndex.keySet());
}
public Map getPrototypeAttributeMapFor(String aPrototypeAttributeName) {
if (prototypeValues == null) {
Map eoPrototypesEntityMap = this.entityPListMap("EOPrototypes");
// no prototypes
if (eoPrototypesEntityMap == null) {
prototypeValues = Collections.emptyMap();
} else {
List eoPrototypeAttributes = (List) eoPrototypesEntityMap.get("attributes");
prototypeValues = new HashMap();
Iterator it = eoPrototypeAttributes.iterator();
while (it.hasNext()) {
Map attrMap = (Map) it.next();
String attrName = (String) attrMap.get("name");
// TODO: why are we copying the original map? can we just use it as is?
Map prototypeAttrMap = new HashMap();
prototypeValues.put(attrName, prototypeAttrMap);
prototypeAttrMap.put("name", attrMap.get("name"));
prototypeAttrMap.put("prototypeName", attrMap.get("prototypeName"));
prototypeAttrMap.put("columnName", attrMap.get("columnName"));
prototypeAttrMap.put("valueClassName", attrMap.get("valueClassName"));
prototypeAttrMap.put("width", attrMap.get("width"));
prototypeAttrMap.put("allowsNull", attrMap.get("allowsNull"));
prototypeAttrMap.put("scale", attrMap.get("scale"));
prototypeAttrMap.put("valueType", attrMap.get("valueType"));
prototypeAttrMap.put("externalType", attrMap.get("externalType"));
}
}
}
Map aMap = (Map) prototypeValues.get(aPrototypeAttributeName);
if (null == aMap)
aMap = Collections.EMPTY_MAP;
return aMap;
}
/** Returns an info map for the entity called entityName
. */
public Map entityPListMap(String entityName) {
return (Map) entityIndex.get(entityName);
}
/**
* Returns the iterator over EOFetchSpecification names for a given entity.
*
* @since 1.1
*/
public Iterator queryNames(String entityName) {
Map queryPlist = (Map) entityQueryIndex.get(entityName);
if (queryPlist == null || queryPlist.isEmpty()) {
return Collections.emptyIterator();
}
return queryPlist.keySet().iterator();
}
/**
* Returns a map containing EOFetchSpecification information for entity name
* and query name. Returns null if no such query is found.
*
* @since 1.1
*/
public Map queryPListMap(String entityName, String queryName) {
Map queryPlist = (Map) entityQueryIndex.get(entityName);
if (queryPlist == null || queryPlist.isEmpty()) {
return null;
}
return (Map) queryPlist.get(queryName);
}
public String entityClass(String entityName, boolean getClientClass) {
if (getClientClass) {
return (String) entityClientClassIndex.get(entityName);
} else {
return (String) entityClassIndex.get(entityName);
}
}
/** Loads EOModel index and returns it as a map. */
protected Map loadModelIndex() throws Exception {
try (InputStream indexIn = openIndexStream();) {
plistParser.ReInit(indexIn);
return (Map) plistParser.propertyList();
}
}
/**
* Loads EOEntity information and returns it as a map.
*/
protected Map loadEntityIndex(String entityName) throws Exception {
try (InputStream entIn = openEntityStream(entityName);) {
plistParser.ReInit(entIn);
return (Map) plistParser.propertyList();
}
}
/**
* Loads EOFetchSpecification information and returns it as a map.
*/
protected Map loadQueryIndex(String entityName) throws Exception {
InputStream queryIn = null;
// catch file open exceptions since not all entities have query
// files....
try {
queryIn = openQueryStream(entityName);
} catch (IOException ioex) {
return Collections.EMPTY_MAP;
}
try {
plistParser.ReInit(queryIn);
return (Map) plistParser.propertyList();
} finally {
queryIn.close();
}
}
/** Returns EOModel name based on its path. */
protected String findModelName(String path) {
// strip trailing slashes
if (path.endsWith("/") || path.endsWith("\\")) {
path = path.substring(0, path.length() - 1);
}
// strip path components
int i1 = path.lastIndexOf("/");
int i2 = path.lastIndexOf("\\");
int i = (i1 > i2) ? i1 : i2;
if (i >= 0) {
path = path.substring(i + 1);
}
// strip .eomodeld suffix
if (path.endsWith(".eomodeld")) {
path = path.substring(0, path.length() - ".eomodeld".length());
}
return path;
}
/**
* Returns InputStream to read an EOModel index file.
*/
protected InputStream openIndexStream() throws Exception {
return new URL(modelUrl, "index.eomodeld").openStream();
}
/**
* Returns InputStream to read an EOEntity plist file.
*
* @param entityName
* name of EOEntity to be loaded.
* @return InputStream to read an EOEntity plist file or null if
* entityname.plist
file can not be located.
*/
protected InputStream openEntityStream(String entityName) throws Exception {
return new URL(modelUrl, entityName + ".plist").openStream();
}
/**
* Returns InputStream to read an EOFetchSpecification plist file.
*
* @param entityName
* name of EOEntity to be loaded.
* @return InputStream to read an EOEntity plist file or null if
* entityname.plist
file can not be located.
*/
protected InputStream openQueryStream(String entityName) throws Exception {
return new URL(modelUrl, entityName + ".fspec").openStream();
}
}