All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy