/* This file is part of Green.
*
* Copyright (C) 2005 The Research Foundation of State University of New York
* All Rights Under Copyright Reserved, The Research Foundation of S.U.N.Y.
*
* Green is free software, licensed under the terms of the Eclipse
* Public License, version 1.0. The license is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package edu.buffalo.cse.green.editor.model;
import static edu.buffalo.cse.green.GreenException.GRERR_TYPE_UNSUPPORTED;
import static edu.buffalo.cse.green.constants.CodeConstants.Model.DEFAULT_X_LOCATION;
import static edu.buffalo.cse.green.constants.CodeConstants.Model.DEFAULT_Y_LOCATION;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_HEIGHT;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_NAME;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_WIDTH;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_X;
import static edu.buffalo.cse.green.constants.XMLConstants.XML_TYPE_Y;
import static edu.buffalo.cse.green.editor.controller.PropertyChange.IncomingRelationship;
import static edu.buffalo.cse.green.editor.controller.PropertyChange.OutgoingRelationship;
import static edu.buffalo.cse.green.editor.controller.PropertyChange.Refresh;
import static edu.buffalo.cse.green.preferences.PreferenceInitializer.P_DISPLAY_FQN_TYPE_NAMES;
import static org.eclipse.jdt.ui.refactoring.RenameSupport.UPDATE_REFERENCES;
import java.lang.reflect.Constructor;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.refactoring.RenameSupport;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchWindow;
import edu.buffalo.cse.green.GreenException;
import edu.buffalo.cse.green.PlugIn;
import edu.buffalo.cse.green.dialogs.wizards.NewElementWizard;
import edu.buffalo.cse.green.editor.DiagramEditor;
import edu.buffalo.cse.green.editor.action.ContextAction;
import edu.buffalo.cse.green.editor.controller.TypePart;
import edu.buffalo.cse.green.editor.model.commands.DeleteCommand;
import edu.buffalo.cse.green.editor.model.commands.DeleteTypeCommand;
import edu.buffalo.cse.green.editor.model.commands.HideTypeCommand;
import edu.buffalo.cse.green.types.ITypeProperties;
import edu.buffalo.cse.green.xml.XMLConverter;
//Compartment visibility stuff
//import org.eclipse.draw2d.geometry.Rectangle;
//import edu.buffalo.cse.green.editor.DiagramEditor;
//import static edu.buffalo.cse.green.editor.controller.PropertyChange.Size;
//import edu.buffalo.cse.green.editor.controller.AbstractPart;
//import edu.buffalo.cse.green.editor.controller.CompartmentPart;
//import edu.buffalo.cse.green.editor.model.commands.SetConstraintCommand;
/**
* Represents a type in the model hierarchy.
*
* @author bcmartin
*/
public class TypeModel extends MemberModel<CompartmentModel, RootModel, IType> {
private HashSet<RelationshipModel> _incomingEdges;
private HashSet<RelationshipModel> _outgoingEdges;
private HashSet<RelationshipModel> _implicitRelationships;
private CompartmentModel _nameCompartment;
private CompartmentModel _fieldCompartment;
private CompartmentModel _methodCompartment;
//Visibility of individual compartments was an unsuccessful attempt
//at showing portions of the diagram. Zero-sized compartments
//created much more problems than they're worth, so the solution
//for smaller class boxes was made with filters
// private boolean _fieldsAreVisible;
//
// private boolean _methodsAreVisible;
public TypeModel() {
this(null);
}
protected TypeModel(IType type) {
super(type);
// set defaults
// _fieldsAreVisible = true;
// _methodsAreVisible = true;
_incomingEdges = new HashSet<RelationshipModel>();
_outgoingEdges = new HashSet<RelationshipModel>();
_implicitRelationships = new HashSet<RelationshipModel>();
_nameCompartment = CompartmentModel.newTypeCompartment();
setLocation(new Point(DEFAULT_X_LOCATION, DEFAULT_Y_LOCATION));
setSize(new Dimension(-1, -1));
// add the box for the name
addChild(_nameCompartment);
if (type != null) {
ITypeProperties properties = getTypeProperties(type);
// add the box for fields
if (properties.hasFieldCompartment()) {
_fieldCompartment = CompartmentModel.newFieldCompartment();
addChild(_fieldCompartment);
}
// add the box for methods
if (properties.hasMethodCompartment()) {
_methodCompartment = CompartmentModel.newMethodCompartment();
addChild(_methodCompartment);
}
}
}
// /**
// * Sets the visibility of individual compartments
// *
// * @param field true to show fields
// * @param method true to show methods
// */
// public void setCompartmentVisibility(boolean field, boolean method) {
//// _fieldsAreVisible = field;
//// _methodsAreVisible = method;
// if(_fieldCompartment != null) {
//// SetConstraintCommand command = new SetConstraintCommand(_fieldCompartment);
// if(field) {
// System.out.println("=================================================");
// System.out.println("Size: " + _fieldCompartment.getSize());
// System.out.println("Bounds:" + _fieldCompartment.getBounds());
// System.out.println("Drawn Size: " + _fieldCompartment.getDrawnSize());
// _fieldCompartment.setSize(new Dimension(0, 0));
// this.forceRefesh();
//// this.setSize(width, height)
//// this.
//// _fieldCompartment.setVisible(true);
//// _fieldCompartment.setDrawnSize(new Dimension(0, 0));
// CompartmentPart p = (CompartmentPart) DiagramEditor.getActiveEditor().getRootPart().getPartFromModel(_fieldCompartment);
// p.getFigure().setSize(new Dimension(0, 0));
//
//
//// _fieldCompartment.removeChildren();
// System.out.println("-------------------------------------------------");
// System.out.println("Size: " + _fieldCompartment.getSize());
// System.out.println("Bounds:" + _fieldCompartment.getBounds());
// System.out.println("Drawn Size: " + _fieldCompartment.getDrawnSize());
// _fieldCompartment.refresh();
//// _fieldCompartment.firePropertyChange(Size, _fieldCompartment.getSize(), new Dimension( -1, -1));
//// updateFields();
//// command.setBounds(new Rectangle(_fieldCompartment.getLocation(), new Dimension(-1, -1)));
// this.refresh();
// }
// else {
// _fieldCompartment.setVisible(false);
//// _fieldCompartment.setDrawnSize(new Dimension(0, 0));
//// _fieldCompartment.se
// _fieldCompartment.setSize(new Dimension(0, 0));
//// _fieldCompartment.firePropertyChange(Size, _fieldCompartment.getSize(), new Dimension( 0, 0));
//// updateFields();
//// command.setBounds(new Rectangle(_fieldCompartment.getLocation(), new Dimension(0, 0)));
// this.refresh();
// }
//// DiagramEditor.getActiveEditor().execute(command);
//
// }
// if(_methodCompartment != null) {
// SetConstraintCommand command = new SetConstraintCommand(_methodCompartment);
// if(method) {
//// _methodCompartment.setSize(new Dimension(-1, -1));
// _methodCompartment.setVisible(true);
//// command.setBounds(new Rectangle(_methodCompartment.getLocation(), new Dimension(-1, -1)));
// }
// else {
//// _methodCompartment.setSize(new Dimension(0, 0));
// _methodCompartment.setVisible(false);
//// command.setBounds(new Rectangle(_methodCompartment.getLocation(), new Dimension(0, 0)));
// }
//// DiagramEditor.getActiveEditor().execute(command);
// }
// }
/**
* Adds a <code>CompartmentModel</code> child.
*
* @param compartment - The child.
*/
private void addChild(CompartmentModel compartment) {
addChild(compartment, null);
}
/**
* Adds a <code>FieldModel</code> child.
*
* @param model - The child.
*/
public void addChild(FieldModel model) {
CompartmentModel cm = getFieldCompartmentModel();
if (cm == null) return;
if (!PlugIn.filterMember(model)) {
cm.addChild(model);
} else {
cm.removeChild(model);
}
}
/**
* Adds a <code>MethodModel</code> child.
*
* @param model - The child.
*/
public void addChild(MethodModel model) {
if (!PlugIn.filterMember(model)) {
getMethodCompartmentModel().addChild(model);
} else {
getMethodCompartmentModel().removeChild(model);
}
}
/**
* @return The caption to display on the label for the type. The display
* name will be either the simple name or the fully qualified name, which is
* at the user's discretion.
*/
public String getDisplayName() {
boolean fqn = PlugIn.getBooleanPreference(P_DISPLAY_FQN_TYPE_NAMES);
return fqn ? getType().getFullyQualifiedName() :
getType().getElementName();
}
/**
* Updates the methods compartment.
*/
public void updateMethods() {
//FIXME Method filter causes NPE in this method
//Must look into this to make sure the solution of destroying the
//old compartment and making new ones doesn't **** up...create
//grossly undesirable side effects in...our underlying model.
// if(!_methodsAreVisible) return;
CompartmentModel compartment = getMethodCompartmentModel();
if (compartment == null) return;
compartment.removeChildren();
// removeChild(_methodCompartment);
// _methodCompartment = CompartmentModel.newMethodCompartment();
// addChild(_methodCompartment);
try {
IMethod[] methods = getType().getMethods();
for (int j = 0; j < methods.length; j++) {
MethodModel mModel = new MethodModel(methods[j]);
addChild(mModel);
}
} catch (JavaModelException e) {
e.printStackTrace();
}
}
/**
* Updates the fields compartment.
*/
public void updateFields() {
//FIXME Field filter causes NPE in this method
//Must look into this to make sure the solution of destroying the
//old compartment and making new ones doesn't **** up...create
//grossly undesirable side effects in...our underlying model.
// if(!_fieldsAreVisible) return;
CompartmentModel compartment = getFieldCompartmentModel();
if (compartment == null) return;
compartment.removeChildren();
// removeChild(_fieldCompartment);
// _fieldCompartment = CompartmentModel.newFieldCompartment();
// addChild(_fieldCompartment);
try {
IField[] fields = getType().getFields();
for (int j = 0; j < fields.length; j++) {
FieldModel fModel = new FieldModel(fields[j]);
addChild(fModel);
}
} catch (JavaModelException e) {
e.printStackTrace();
}
}
/**
* Gets a set of incoming edges.
*/
public Set<RelationshipModel> getIncomingEdges() {
return _incomingEdges;
}
/**
* Gets a set of outgoing edges.
*/
public Set<RelationshipModel> getOutgoingEdges() {
return _outgoingEdges;
}
/**
* Adds an incoming <code>RelationshipModel</code> edge.
*
* @param relationshipModel - The edge to add.
*/
public void addIncomingEdge(RelationshipModel relationshipModel) {
_incomingEdges.add(relationshipModel);
firePropertyChange(IncomingRelationship, null, relationshipModel);
}
/**
* Removes an incoming <code>RelationshipModel</code> edge.
*
* @param relationshipModel - The edge to add.
*/
public void removeIncomingEdge(RelationshipModel relationshipModel) {
if (_incomingEdges.contains(relationshipModel)) {
_incomingEdges.remove(relationshipModel);
firePropertyChange(IncomingRelationship, relationshipModel,
null);
}
}
/**
* Adds an outgoing <code>RelationshipModel</code> edge.
*
* This method is used by DiagramRootModel.addRelationship(...) and should
* not be called by other methods.
*/
public void addOutgoingEdge(RelationshipModel edge) {
_outgoingEdges.add(edge);
firePropertyChange(OutgoingRelationship, null, edge);
}
/**
* Removes an outgoing <code>RelationshipModel</code> edge.
*
* This method is used by DiagramRootModel.addRelationship(...) and should
* not be called by other methods.
*/
public void removeOutgoingEdge(RelationshipModel edge) {
if (_outgoingEdges.remove(edge)) {
firePropertyChange(OutgoingRelationship, edge, null);
}
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#toXML(edu.buffalo.cse.green.xml.XMLConverter)
*/
public void toXML(XMLConverter converter) {
//LOOKINTO [Can be removed if refactoring is done through extension point.] Refactoring in DIA files, this writes handle to XML.
converter.pushHeader(XML_TYPE);
converter.writeKey(XML_TYPE_NAME, getType().getHandleIdentifier());
converter.writeKey(XML_TYPE_HEIGHT, "" + getSize().height);
converter.writeKey(XML_TYPE_WIDTH, "" + getSize().width);
converter.writeKey(XML_TYPE_X, "" + getLocation().x);
converter.writeKey(XML_TYPE_Y, "" + getLocation().y);
super.toXML(converter);
converter.popHeader();
}
/**
* @return The list of hidden relationships.
*/
public Set<RelationshipModel> getImplicitRelationships() {
return _implicitRelationships;
}
/**
* Makes a relationship implicit.
*
* @param rModel - The relationship.
*/
public void addImplicitRelationship(RelationshipModel rModel) {
_implicitRelationships.add(rModel);
firePropertyChange(Refresh);
}
/**
* Makes a relationship explicit.
*
* @param rModel - The relationship.
*/
public void removeImplicitRelationship(RelationshipModel rModel) {
_implicitRelationships.remove(rModel);
if (_implicitRelationships.size() == 0) {
firePropertyChange(Refresh);
}
}
/**
* @return The <code>IType</code> modeled by this class.
*/
public IType getType() {
return (IType) getMember();
}
/**
* @return The compartment that contains the label that holds the name of
* the type.
*/
public CompartmentModel getNameCompartmentModel() {
return _nameCompartment;
}
/**
* @return The compartment that contains the label that holds the fields of
* the type.
*/
public CompartmentModel getFieldCompartmentModel() {
return _fieldCompartment;
}
/**
* @return The compartment that contains the label that holds the methods of
* the type.
*/
public CompartmentModel getMethodCompartmentModel() {
return _methodCompartment;
}
/**
* @return 'true' if the modeled type is a class, false otherwise
*/
public boolean isClass() {
if (!getMember().exists()) return false;
try {
return getType().isClass();
} catch (JavaModelException e) {
e.printStackTrace();
return false;
}
}
/**
* @return True if the modeled type is a class, false otherwise.
*/
public boolean isInterface() throws JavaModelException {
if (!getMember().exists()) return false;
return getType().isInterface();
}
/**
* @return True if the modeled type is abstract, false otherwise.
*/
public boolean isAbstract() throws JavaModelException {
if (!getMember().exists()) return false;
return Flags.isAbstract(getType().getFlags());
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#getContextMenuFlag()
*/
public int getContextMenuFlag() {
return ContextAction.CM_TYPE;
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#removeFromParent()
*/
public void removeFromParent() {
removeChildren();
getRootModel().removeChildModel(this);
super.removeFromParent();
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#getDeleteCommand(edu.buffalo.cse.green.editor.DiagramEditor)
*/
public DeleteCommand getDeleteCommand(DiagramEditor editor) {
return new DeleteTypeCommand(this);
}
/**
* @param editor - The <code>DiagramEditor</code> containing this model.
*
* @return A command to hide this model.
*/
public Command getHideCommand(DiagramEditor editor) {
return new HideTypeCommand(this);
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#getPartClass()
*/
public Class getPartClass() {
return TypePart.class;
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#setVisible(boolean)
*/
public void setVisible(boolean value) {
super.setVisible(value);
if (value) {
for (RelationshipModel rModel : getIncomingEdges()) {
rModel.setVisible(true);
}
for (RelationshipModel rModel : getOutgoingEdges()) {
rModel.setVisible(true);
}
}
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#getTypeModel()
*/
public TypeModel getTypeModel() {
return this;
}
/**
* @see edu.buffalo.cse.green.editor.model.MemberModel#getRenameSupport()
*/
public RenameSupport getRenameSupport() throws CoreException {
return RenameSupport.create(getType(), "", UPDATE_REFERENCES);
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#invokeCreationDialog(org.eclipse.gef.palette.ToolEntry)
*/
public int invokeCreationDialog(ToolEntry tool) {
ITypeProperties properties = getTypeProperties(
tool.getLabel());
Class klass = properties.getDialogClass();
Constructor<?> constructor = klass.getConstructors()[0];
if (constructor.getParameterTypes().length != 0) {
GreenException.illegalOperation(
"Constructor must not have any parameters");
}
try {
NewElementWizard wizard =
(NewElementWizard) constructor.newInstance(new Object[] {});
wizard.setModel(this);
wizard.init(PlugIn.getDefault().getWorkbench(),
getCurrentSelection());
WizardDialog dialog =
new WizardDialog(PlugIn.getDefaultShell(), wizard);
dialog.setMinimumPageSize(300, 500);
dialog.create();
dialog.open();
return dialog.getReturnCode();
} catch (Exception e) {
e.printStackTrace();
return WizardDialog.CANCEL;
}
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#createNewInstance(edu.buffalo.cse.green.editor.model.AbstractModel)
*/
public void createNewInstance(AbstractModel model) {
RootModel root = model.getRootModel();
TypeModel typeModel = (TypeModel) model;
typeModel = root.createTypeModel(typeModel.getType());
typeModel.setLocation(model.getLocation());
typeModel.setSize(model.getSize());
root.updateRelationships();
}
/**
* @return The current selection.
*/
private IStructuredSelection getCurrentSelection() {
IWorkbenchWindow[] windows = PlugIn.getDefault().getWorkbench()
.getWorkbenchWindows();
for (int i = 0; i < windows.length; i++) {
IViewPart packExplorer = windows[i].getPages()[0]
.findView(JavaUI.ID_PACKAGES);
if (packExplorer != null) {
ISelection selection = (ISelection) packExplorer.getViewSite()
.getSelectionProvider().getSelection();
if (selection instanceof IStructuredSelection) { return (IStructuredSelection) selection; }
}
}
return null;
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#assertValid()
*/
public void assertValid() {
GreenException.illegalOperation(getRootModel().isValidTypeModel(this));
}
/**
* @see edu.buffalo.cse.green.editor.model.AbstractModel#refresh()
*/
public void refresh() {
if (getMember().exists()) {
updateFields();
updateMethods();
}
super.refresh();
}
/**
* @param type - The given <code>IType</code>.
* @return The plugin class that specifies properties of how the given type
* integrates with Green's editor.
*/
private static ITypeProperties getTypeProperties(IType type) {
return getTypeProperties(getTypeAsString(type));
}
/**
* @param key - The <code>String</code> representation of the kind of type
* (e.g. class, enum).
* @return The plugin class that specifies properties of how the given type
* integrates with Green's editor.
*/
private static ITypeProperties getTypeProperties(String key) {
ITypeProperties properties = PlugIn.getTypeProperties().get(key);
if (properties == null) {
GreenException.illegalOperation(GRERR_TYPE_UNSUPPORTED);
}
return properties;
}
/**
* @param type - The given <code>IType</code>.
* @return A string representation of the kind of type (e.g. class, enum).
*/
private static String getTypeAsString(IType type) {
for (ITypeProperties typeProp : PlugIn.getAvailableTypes()) {
if (typeProp.supportsType(type)) {
return typeProp.getLabel();
}
}
GreenException.illegalOperation(GRERR_TYPE_UNSUPPORTED);
return null;
}
}