/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.javaparser;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InvalidNameException;
import org.openflexo.foundation.Inspectors;
import org.openflexo.foundation.dm.DMEntity;
import org.openflexo.foundation.dm.DMMethod;
import org.openflexo.foundation.dm.DMObject;
import org.openflexo.foundation.dm.DMProperty;
import org.openflexo.foundation.dm.DMSet.PackageReference.ClassReference;
import org.openflexo.foundation.dm.DMSet.PackageReference.ClassReference.MethodReference;
import org.openflexo.foundation.dm.DMSet.PackageReference.ClassReference.PropertyReference;
import org.openflexo.foundation.dm.DMType;
import org.openflexo.foundation.dm.DuplicateClassNameException;
import org.openflexo.foundation.dm.DuplicateMethodSignatureException;
import org.openflexo.foundation.dm.DuplicatePropertyNameException;
import org.openflexo.foundation.dm.javaparser.ParsedJavaClass;
import org.openflexo.foundation.dm.javaparser.ParsedJavaElement;
import org.openflexo.javaparser.FJPTypeResolver.UnresolvedTypeException;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.Type;
public class FJPJavaClass extends FJPJavaEntity implements ParsedJavaClass {
private static final Logger logger = Logger.getLogger(FJPJavaClass.class.getPackage().getName());
private JavaClass _qdJavaClass;
public FJPJavaClass(JavaClass qdJavaClass, FJPJavaSource aJavaSource) {
super(qdJavaClass, aJavaSource);
_qdJavaClass = qdJavaClass;
}
@Override
public String getName() {
return _qdJavaClass.getName();
}
public Type asType() {
return _qdJavaClass.asType();
}
private FJPJavaClass[] _derivedClasses;
public FJPJavaClass[] getDerivedClasses() {
if (_derivedClasses == null) {
_derivedClasses = retrieveClasses(_qdJavaClass.getDerivedClasses());
}
return _derivedClasses;
}
@Override
public FJPJavaField getFieldByName(String name) {
return getField(_qdJavaClass.getFieldByName(name));
}
private FJPJavaField[] _fields;
public FJPJavaField[] getFields() {
if (_fields == null) {
_fields = retrieveFields(_qdJavaClass.getFields());
}
return _fields;
}
@Override
public String getFullyQualifiedName() {
return _qdJavaClass.getFullyQualifiedName();
}
private FJPJavaClass[] _implementedInterfaces;
public FJPJavaClass[] getImplementedInterfaces() {
if (_implementedInterfaces == null) {
_implementedInterfaces = retrieveClasses(_qdJavaClass.getImplementedInterfaces());
}
return _implementedInterfaces;
}
public Type[] getImplements() {
return _qdJavaClass.getImplements();
}
private String implementsAsString = null;
public String getImplementsAsString() {
if (implementsAsString == null) {
boolean isFirst = true;
StringBuffer sb = new StringBuffer();
for (Type t : getImplements()) {
sb.append((isFirst ? "" : ",") + t.toString());
isFirst = false;
}
implementsAsString = sb.toString();
}
return implementsAsString;
}
private FJPJavaMethod[] _methods;
public FJPJavaMethod[] getMethods() {
if (_methods == null) {
_methods = retrieveMethods(_qdJavaClass.getMethods());
}
return _methods;
}
public FJPJavaMethod getMethodBySignature(String name, DMType[] parameterTypes, boolean superclasses) {
return getMethod(_qdJavaClass.getMethodBySignature(name, parameterTypes, superclasses));
}
@Override
public FJPJavaMethod getMethodBySignature(String signature) {
// logger.info("Looking for "+signature);
// First look for full qualified
for (FJPJavaMethod m : getMethods()) {
// logger.info("Compare: "+signature+" and "+m.getCallSignature());
if (m.getCallSignature().equals(signature)) {
return m;
}
}
// Not found
// Try without qualifiers
String unqualifiedSignature = FJPJavaMethod.unqualifySignature(signature);
for (FJPJavaMethod m : getMethods()) {
String us = FJPJavaMethod.unqualifySignature(m.getCallSignature());
// logger.info("Compare: "+unqualifiedSignature+" and "+us);
if (unqualifiedSignature.equals(us)) {
return m;
}
}
return null;
}
public FJPJavaMethod getMethodBySignature(String name, DMType... parameterTypes) {
return getMethod(_qdJavaClass.getMethodBySignature(name, parameterTypes));
}
public FJPJavaMethod[] getMethods(boolean name) {
return retrieveMethods(_qdJavaClass.getMethods(name));
}
public FJPJavaMethod[] getMethodsBySignature(String name, DMType[] parameterTypes, boolean superclasses) {
return retrieveMethods(_qdJavaClass.getMethodsBySignature(name, parameterTypes, superclasses));
}
public FJPJavaClass getNestedClassByName(String arg0) {
return getClass(_qdJavaClass.getNestedClassByName(arg0));
}
private FJPJavaClass[] _nestedClasses;
public FJPJavaClass[] getNestedClasses() {
if (_nestedClasses == null) {
_nestedClasses = retrieveClasses(_qdJavaClass.getNestedClasses());
}
return _nestedClasses;
}
public String getPackage() {
return _qdJavaClass.getPackage();
}
public DMType getSuperClass() {
if (_qdJavaClass != null) {
return (DMType) _qdJavaClass.getSuperClass();
}
return null;
}
public FJPJavaClass getSuperJavaClass() {
return getClass(_qdJavaClass.getSuperJavaClass());
}
public String getSuperClassAsString() {
if (getSuperClass() != null) {
return getSuperClass().getStringRepresentation();
}
return "java.lang.Object";
}
public boolean isEnum() {
return _qdJavaClass.isEnum();
}
public boolean isInner() {
return _qdJavaClass.isInner();
}
public boolean isInterface() {
return _qdJavaClass.isInterface();
}
@Override
public String getInspectorName() {
return Inspectors.CG.JAVA_CLASS_INSPECTOR;
}
private Vector<FJPJavaEntity> orderedChildren = null;
public Vector<FJPJavaEntity> getOrderedChildren() {
if (orderedChildren == null) {
orderedChildren = new Vector<FJPJavaEntity>();
for (FJPJavaField f : getFields()) {
orderedChildren.add(f);
}
for (FJPJavaMethod m : getMethods()) {
orderedChildren.add(m);
}
for (FJPJavaClass c : getNestedClasses()) {
orderedChildren.add(c);
}
Collections.sort(orderedChildren, new Comparator<FJPJavaEntity>() {
@Override
public int compare(FJPJavaEntity o1, FJPJavaEntity o2) {
return o1.getLineNumber() - o2.getLineNumber();
}
});
}
return orderedChildren;
}
@Override
public Vector<? extends ParsedJavaElement> getMembers() {
return getOrderedChildren();
}
@Override
public int getLinesCount() {
return 1;
}
private void registerMethods(DMEntity entity, FJPDMSet context, FJPJavaSource source, Vector<String> excludedSignatures) {
Vector<DMMethod> allMethods = FJPDMMapper.searchForMethods(this, entity.getDMModel(), context, source, true, excludedSignatures);
for (DMMethod m : allMethods) {
entity.registerMethod(m);
}
;
}
private Vector registerProperties(DMEntity entity, FJPDMSet context, FJPJavaSource source, boolean includesGetOnlyProperties) {
Vector<String> excludedSignatures = new Vector<String>();
for (Enumeration en = FJPDMMapper.searchForProperties(this, entity.getDMModel(), context, source, includesGetOnlyProperties, true,
excludedSignatures).elements(); en.hasMoreElements();) {
DMProperty next = (DMProperty) en.nextElement();
entity.registerProperty(next, false);
}
;
return excludedSignatures;
}
private void updateProperty(DMEntity entity, DMProperty property, FJPDMSet context, FJPJavaSource source)
throws FJPTypeResolver.CrossReferencedEntitiesException, InvalidNameException, DuplicatePropertyNameException {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Update property " + property);
}
DMProperty updatedProperty = FJPDMMapper.makeProperty(this, property.getName(), entity.getDMModel(), context, source, true, true,
null);
if (updatedProperty == null) {
logger.warning("Could not instanciate property " + property.getName() + " this should not happen.");
return;
}
// For now, don't change implementation type from model reinjection
updatedProperty.setImplementationType(property.getImplementationType());
// Nor read-only property
updatedProperty.setIsReadOnly(property.getIsReadOnly());
logger.info("Update property " + property);
property.update(updatedProperty, true);
}
private DMProperty createProperty(DMEntity entity, PropertyReference propertyReference, FJPDMSet context, FJPJavaSource source)
throws FJPTypeResolver.CrossReferencedEntitiesException {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Create property " + propertyReference.getName());
}
DMProperty returnedProperty = FJPDMMapper.makeProperty(this, propertyReference.getName(), entity.getDMModel(), context, source,
true, true, null);
logger.info("For property " + propertyReference.getName() + " Create property " + returnedProperty);
if (returnedProperty != null) {
returnedProperty.allowModifiedPropagation();
entity.registerProperty(returnedProperty, false);
}
return returnedProperty;
}
private void updateMethod(DMEntity entity, DMMethod method, FJPDMSet context, FJPJavaSource source)
throws FJPTypeResolver.CrossReferencedEntitiesException {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Update method " + method);
}
DMMethod updatedMethod = FJPDMMapper.makeMethod(this, method.getSignature(), entity.getDMModel(), context, source, true);
if (updatedMethod == null) {
logger.info("Delete method " + method);
method.delete();
} else {
logger.info("Update method " + method);
try {
method.update(updatedMethod, true);
} catch (DuplicateMethodSignatureException e) {
e.printStackTrace();
}
}
}
private DMMethod createMethod(DMEntity entity, MethodReference methodReference, FJPDMSet context, FJPJavaSource source)
throws FJPTypeResolver.CrossReferencedEntitiesException {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Create method " + methodReference.getSignature());
}
DMMethod returnedMethod = FJPDMMapper.makeMethod(this, methodReference.getSignature(), entity.getDMModel(), context, source, true);
logger.info("For method " + methodReference.getSignature() + " Create method " + returnedMethod);
if (returnedMethod != null) {
returnedMethod.allowModifiedPropagation();
entity.registerMethod(returnedMethod);
}
return returnedMethod;
}
private void updateEntity(DMEntity entity, FJPDMSet context, FJPJavaSource source) throws DuplicateClassNameException,
InvalidNameException, FJPTypeResolver.CrossReferencedEntitiesException, FJPTypeResolver.UnresolvedTypeException {
if (entity == null) {
logger.warning("Entity is null !");
return;
}
entity.setEntityPackageName(getPackage());
entity.setEntityClassName(getName());
DMType parentType = getSuperClass();
entity.setParentType(parentType, true);
if (FJPTypeResolver.isResolvable(parentType, entity.getDMModel(), context, source)) {
FJPTypeResolver.resolveEntity(parentType, entity.getDMModel(), context, source, true);
} else {
throw new FJPTypeResolver.UnresolvedTypeException(parentType);
}
/*if (parentType != null) {
if (FJPTypeResolver.isResolvable(parentType, entity.getDMModel(), context, source)) {
DMEntity resolvedParentEntity = FJPTypeResolver.resolveEntity(parentType, entity.getDMModel(), context, source, true);
if (resolvedParentEntity == null) throw new FJPTypeResolver.UnresolvedTypeException(parentType);
entity.setParentEntity(resolvedParentEntity);
}
else {
throw new FJPTypeResolver.UnresolvedTypeException(parentType);
}
}*/
}
/**
* Update this entity given a supplied set of properties and methods to include
*
* @param aClassReference
* @throws UnresolvedTypeException
*/
public Vector<DMObject> update(DMEntity entity, ClassReference aClassReference, FJPDMSet context, FJPJavaSource source)
throws FJPTypeResolver.CrossReferencedEntitiesException, FJPTypeResolver.UnresolvedTypeException {
if (entity == null || aClassReference == null) {
logger.warning("Could not update: could not find entity or class reference !");
} else {
Vector<DMObject> newObjects = new Vector<DMObject>();
// First update package and class name, and parent class if different
try {
updateEntity(entity, context, source);
} catch (FJPTypeResolver.CrossReferencedEntitiesException e) {
// TODO implements this
logger.warning("Needs to be stored and invoked in second step for cross referenced entities...");
throw e;
} catch (DuplicateClassNameException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidNameException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Then what's about properties ?
Vector<DMProperty> propertiesToDelete = new Vector<DMProperty>(entity.getProperties().values());
for (PropertyReference nextPropertyRef : aClassReference.getProperties()) {
if (nextPropertyRef.isSelected()) {
DMProperty property = entity.getDMProperty(nextPropertyRef.getName());
if (property != null) {
// A selected property already declared, update it
try {
updateProperty(entity, property, context, source);
} catch (FJPTypeResolver.CrossReferencedEntitiesException e) {
// TODO implements this
logger.warning("Needs to be stored and invoked in second step for cross referenced entities...");
} catch (InvalidNameException e) {
// TODO implements this
logger.warning(e.getMessage());
} catch (DuplicatePropertyNameException e) {
e.printStackTrace();
logger.warning(e.getMessage());
}
propertiesToDelete.remove(property);
} else {
// This is a newly imported property, creates it
try {
newObjects.add(createProperty(entity, nextPropertyRef, context, source));
} catch (FJPTypeResolver.CrossReferencedEntitiesException e) {
// TODO implements this
logger.warning("Needs to be stored and invoked in second step for cross referenced entities...");
}
}
}
}
for (Enumeration en = new Vector<DMProperty>(propertiesToDelete).elements(); en.hasMoreElements();) {
DMProperty toDelete = (DMProperty) en.nextElement();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Delete property " + toDelete);
}
toDelete.delete();
}
// Then what's about methods ?
Vector<DMMethod> methodsToDelete = new Vector<DMMethod>(entity.getMethods().values());
for (MethodReference nextMethodRef : aClassReference.getMethods()) {
if (nextMethodRef.isSelected()) {
DMMethod method = entity.getDeclaredMethod(nextMethodRef.getSignature());
if (method != null) {
// A selected method already declared, update it
try {
updateMethod(entity, method, context, source);
} catch (FJPTypeResolver.CrossReferencedEntitiesException e) {
// TODO implements this
logger.warning("Needs to be stored and invoked in second step for cross referenced entities...");
}
methodsToDelete.remove(method);
} else {
// This is a newly imported property, creates it
try {
newObjects.add(createMethod(entity, nextMethodRef, context, source));
} catch (FJPTypeResolver.CrossReferencedEntitiesException e) {
// TODO implements this
logger.warning("Needs to be stored and invoked in second step for cross referenced entities...");
}
}
}
}
for (Enumeration en = new Vector<DMMethod>(methodsToDelete).elements(); en.hasMoreElements();) {
DMMethod toDelete = (DMMethod) en.nextElement();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Delete method " + toDelete);
}
toDelete.delete();
}
logger.info("Updated " + getName());
return newObjects;
}
return null;
}
@Override
public String toString() {
return getFullyQualifiedName();
}
@Override
public String getUniqueIdentifier() {
return getFullyQualifiedName();
}
}