/*==========================================================================*\ | $Id: KeyPathParser.java,v 1.2 2012/03/28 13:48:08 stedwar2 Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2006-2012 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT 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 General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.core; import com.webobjects.eoaccess.EOEntityClassDescription; import com.webobjects.eocontrol.EOClassDescription; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDictionary; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import org.webcat.core.WCProperties; //------------------------------------------------------------------------- /** * A KVC parser for use in queries. * * @author Tony Allevato * @author Last changed by $Author: stedwar2 $ * @version $Revision: 1.2 $, $Date: 2012/03/28 13:48:08 $ */ public class KeyPathParser { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Create an object. * @param entity The entity from which the path originates * @param keyPath The KVC path */ public KeyPathParser(String entity, String keyPath) { this(entity, keyPath, 0); } // ---------------------------------------------------------- /** * Create an object. * @param entity The entity from which the path originates * @param keyPath The KVC path * @param skipEnd If non-zero, indicates how many of the final segments * of the key path to ignore */ public KeyPathParser(String entity, String keyPath, int skipEnd) { this.keyPath = keyPath; Class<?> klass = null; EOClassDescription classDesc = EOClassDescription.classDescriptionForEntityName(entity); if (classDesc instanceof EOEntityClassDescription) { EOEntityClassDescription entDesc = (EOEntityClassDescription)classDesc; try { klass = Class.forName(entDesc.entity().className()); } catch (Exception e) { // Leave klass null } } String[] components = splitKeyPath(keyPath); int i; for (i = 0; i < components.length - skipEnd; i++) { String component = components[i]; NSArray<String> toManyKeys = classDesc.toManyRelationshipKeys(); if (toManyKeys.containsObject(component)) { if (i == components.length - 1) { klass = NSArray.class; } else { klass = null; } break; } EOClassDescription newClassDesc = classDesc.classDescriptionForDestinationKey(component); if (newClassDesc == null) { // If the key isn't a relationship, try to access it as an // attribute. klass = classDesc.classForAttributeKey(component); if (klass == null) { // Last resort -- try to access it as a getter method. klass = classForGetter(classDesc, component); } if (klass != null && (NSDictionary.class.isAssignableFrom(klass) || WCProperties.class.isAssignableFrom(klass))) { if (i < components.length - 1) { klass = Object.class; } else { klass = null; } } /* else if (!isPrimitive(klass)) { klass = null; }*/ break; } else if (newClassDesc instanceof EOEntityClassDescription) { classDesc = newClassDesc; try { EOEntityClassDescription entDesc = (EOEntityClassDescription)classDesc; klass = Class.forName(entDesc.entity().className()); } catch (Exception e) { klass = null; break; } } } theClass = klass; remainingKeyPath = joinStrings(components, i); } //~ Public Methods ........................................................ // ---------------------------------------------------------- public Class<?> theClass() { return theClass; } // ---------------------------------------------------------- public String remainingKeyPath() { return remainingKeyPath; } // ---------------------------------------------------------- public String keyPath() { return keyPath; } //~ Private Methods ....................................................... // ---------------------------------------------------------- /* private boolean isPrimitive(Class<?> klass) { return(klass == String.class || klass == Float.class || klass == Float.class || klass == Double.class || klass == Double.TYPE || klass == Byte.class || klass == Byte.TYPE || klass == Short.class || klass == Short.TYPE || klass == Integer.class || klass == Integer.TYPE || klass == Long.class || klass == Long.TYPE || klass == Boolean.class || klass == Boolean.TYPE || klass == Character.class || klass == Character.TYPE || klass == NSTimestamp.class); } */ // ---------------------------------------------------------- private Class<?> classForGetter(EOClassDescription classDesc, String key) { Class<?> klass = null; if (classDesc instanceof EOEntityClassDescription) { try { EOEntityClassDescription entDesc = (EOEntityClassDescription)classDesc; klass = Class.forName(entDesc.entity().className()); if (klass == null) { return null; } } catch (Exception e) { return null; } } else { return null; } Class<?> returnType = null; // method getFoo() returnType = getterHelper(klass, "get" + initialCap(key)); if (returnType != null) { return returnType; } // method foo() returnType = getterHelper(klass, key); if (returnType != null) { return returnType; } // method isFoo() returnType = getterHelper(klass, "is" + initialCap(key)); if (returnType != null) { return returnType; } // method _getFoo() returnType = getterHelper(klass, "_get" + initialCap(key)); if (returnType != null) { return returnType; } // method _foo() returnType = getterHelper(klass, "_" + key); if (returnType != null) { return returnType; } // method _isFoo() returnType = getterHelper(klass, "_is" + initialCap(key)); if (returnType != null) { return returnType; } // field _foo returnType = fieldHelper(klass, "_" + key); if (returnType != null) { return returnType; } // field _isFoo returnType = fieldHelper(klass, "_is" + initialCap(key)); if (returnType != null) { return returnType; } // field foo returnType = fieldHelper(klass, key); if (returnType != null) { return returnType; } // field isFoo returnType = fieldHelper(klass, "is" + initialCap(key)); if (returnType != null) { return returnType; } // no getter found return null; } // ---------------------------------------------------------- private Class<?> getterHelper(Class<?> klass, String name) { try { Method method = klass.getMethod(name); return method.getReturnType(); } catch (Exception e) { return null; } } // ---------------------------------------------------------- private Class<?> fieldHelper(Class<?> klass, String name) { try { Field field = klass.getField(name); return field.getType(); } catch (Exception e) { return null; } } // ---------------------------------------------------------- private String initialCap(String key) { if (key != null && key.length() > 0) { return "" + Character.toUpperCase(key.charAt(0)) + key.substring(1); } else { return key; } } // ---------------------------------------------------------- private String[] splitKeyPath(String keypath) { ArrayList<String> list = new ArrayList<String>(); while (keypath.indexOf('.') != -1) { int index = keypath.indexOf('.'); list.add(keypath.substring(0, index)); keypath = keypath.substring(index + 1); } list.add(keypath); return list.toArray(new String[list.size()]); } // ---------------------------------------------------------- private String joinStrings(String[] parts, int start) { StringBuffer buffer = new StringBuffer(); if (start < parts.length) { buffer.append(parts[start]); for (int i = start + 1; i < parts.length; i++) { buffer.append('.'); buffer.append(parts[i]); } } return buffer.toString(); } //~ Instance/static variables ............................................. private String keyPath; private String remainingKeyPath; private Class<?> theClass; }