package org.openflexo.view.controller.model;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
import java.util.logging.Level;
import org.openflexo.ApplicationContext;
import org.openflexo.GeneralPreferences;
import org.openflexo.foundation.FlexoEditor;
import org.openflexo.foundation.FlexoModelObject;
import org.openflexo.foundation.cg.CGFile;
import org.openflexo.foundation.dm.DMObject;
import org.openflexo.foundation.rm.FlexoProject;
import org.openflexo.foundation.toc.TOCEntry;
import org.openflexo.foundation.wkf.FlexoProcess;
import org.openflexo.foundation.wkf.ws.MessageEntry;
import org.openflexo.foundation.wkf.ws.ServiceInterface;
import org.openflexo.foundation.wkf.ws.ServiceMessageDefinition;
import org.openflexo.foundation.wkf.ws.ServiceOperation;
import org.openflexo.foundation.ws.WSObject;
import org.openflexo.foundation.ws.WSPortType;
import org.openflexo.module.FlexoModule;
import org.openflexo.module.ModuleLoader;
import org.openflexo.module.ProjectLoader;
import org.openflexo.prefs.FlexoPreferences;
import org.openflexo.swing.layout.MultiSplitLayout.Node;
import org.openflexo.swing.layout.MultiSplitLayoutTypeAdapterFactory;
import org.openflexo.toolbox.ExtendedSet;
import org.openflexo.toolbox.PropertyChangeListenerRegistrationManager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class ControllerModel extends ControllerModelObject implements PropertyChangeListener {
private static final java.util.logging.Logger logger = org.openflexo.logging.FlexoLogger.getLogger(ControllerModel.class.getPackage()
.getName());
public static final String RIGHT_VIEW_VISIBLE = "rightViewVisible";
public static final String LEFT_VIEW_VISIBLE = "leftViewVisible";
public static final String LOCATIONS = "locations";
public static final String PERSPECTIVES = "perspectives";
public static final String EDITORS = "editors";
public static final String CURRENT_LOCATION = "currentLocation";
public static final String CURRENT_OBJECT = "currentObject";
public static final String CURRENT_EDITOR = "currentEditor";
public static final String CURRENT_PERSPECTIVE = "currentPerspective";
private static final Location NO_LOCATION = new Location(null, null, null);
/** Gson instance used to serialize and deserialize layouts of the mainpane multisplitpane */
private Gson gsonLayout;
/** Flag that indicates the current state of the left view */
private boolean leftViewVisible = true;
/** Flag that indicates the current state of the right view */
private boolean rightViewVisible = true;
/** The list of all the available perspectives */
private List<FlexoPerspective> perspectives;
/** List of objects that are represented. This list allows to track observed objects. */
private List<FlexoModelObject> objects;
/** Set of locations that are opened. This set represents all the currently opened module views. */
private ExtendedSet<Location> locations;
/** The stack of locations that were visited */
private Stack<Location> previousHistory;
/**
* The stack of locations that are ahead of the current location. This is only used when the user goes back in history. Whenever the
* users bypasses history navigation, this stack is reset
*/
private Stack<Location> nextHistory;
/** The current location in the history */
private Location currentLocation = NO_LOCATION;
/** Internal flag that indicates if we are currently moving forward in the history */
private boolean isGoingForward = false;
/** Internal flag that indicates if we are currently moving backward in the history */
private boolean isGoingBackward = false;
/** The application context of this controller model */
private ApplicationContext context;
/** The module in which this controller model was created (mainly used to save module specific preferences) */
private FlexoModule module;
/** A property change listener registration manager that keeps track of all the registered {@link PropertyChangeListener} */
private PropertyChangeListenerRegistrationManager registrationManager;
public ControllerModel(ApplicationContext context, FlexoModule module) {
this.context = context;
this.module = module;
registrationManager = new PropertyChangeListenerRegistrationManager();
leftViewVisible = GeneralPreferences.getShowLeftView(module.getShortName());
rightViewVisible = GeneralPreferences.getShowRightView(module.getShortName());
registrationManager.new PropertyChangeListenerRegistration(ProjectLoader.PROJECT_OPENED, this, context.getProjectLoader());
registrationManager.new PropertyChangeListenerRegistration(ProjectLoader.PROJECT_CLOSED, this, context.getProjectLoader());
objects = new ArrayList<FlexoModelObject>();
locations = new ExtendedSet<Location>();
perspectives = new Vector<FlexoPerspective>();
previousHistory = new Stack<Location>();
nextHistory = new Stack<Location>();
}
public ModuleLoader getModuleLoader() {
return context.getModuleLoader();
}
public ProjectLoader getProjectLoader() {
return context.getProjectLoader();
}
public FlexoModule getModule() {
return module;
}
@Override
public void delete() {
for (FlexoPerspective p : perspectives) {
p.delete();
}
perspectives.clear();
registrationManager.delete();
super.delete();
currentLocation = NO_LOCATION;
}
/***************
* PERSPECTIVE *
***************/
/**
* Returns the current perspective.
*
* @return the current perspective
*/
public FlexoPerspective getCurrentPerspective() {
if (currentLocation != null) {
return currentLocation.getPerspective();
} else {
return null;
}
}
public void setCurrentPerspective(FlexoPerspective currentPerspective) {
FlexoModelObject object = getCurrentObject();
if (currentPerspective != null) {
if (object == null || !currentPerspective.hasModuleViewForObject(object)) {
object = currentPerspective.getDefaultObject(object != null ? object : getCurrentProject());
}
}
setCurrentLocation(getCurrentEditor(), object, currentPerspective);
}
public List<FlexoPerspective> getPerspectives() {
return perspectives;
}
public void addToPerspectives(FlexoPerspective perspective) {
if (perspective == null) {
return;
}
perspectives.add(perspective);
getPropertyChangeSupport().firePropertyChange(PERSPECTIVES, null, perspective);
if (getCurrentPerspective() == null) {
setCurrentPerspective(perspective);
}
}
public void removeFromPerspectives(FlexoPerspective perspective) {
perspectives.remove(perspective);
getPropertyChangeSupport().firePropertyChange(PERSPECTIVES, perspective, null);
}
/***********
* EDITORS *
***********/
public FlexoEditor getCurrentEditor() {
if (requiresProject()) {
return currentLocation.getEditor();
} else {
return context.getApplicationEditor();
}
}
public boolean requiresProject() {
return module.getModule().requireProject();
}
public void setCurrentEditor(FlexoEditor currentEditor) {
if (currentEditor != getCurrentEditor()) {
if (currentEditor == null || currentEditor.getProject() == null || isSelectableProject(currentEditor.getProject())) {
Location location = getLastLocationForEditor(currentEditor, null);
if (location != null && location != NO_LOCATION) {
setCurrentLocation(location);
} else {
FlexoModelObject object = null;
if (getCurrentEditor() == null && currentEditor != null && currentEditor.getProject() != null
&& getCurrentPerspective() != null) {
object = getCurrentPerspective().getDefaultObject(currentEditor.getProject());
}
setCurrentLocation(currentEditor, object, getCurrentPerspective());
}
}
}
}
public FlexoProject getCurrentProject() {
return getCurrentEditor() != null ? getCurrentEditor().getProject() : null;
}
public void setCurrentProject(FlexoProject project) {
setCurrentEditor(project != null ? context.getProjectLoader().getEditorForProject(project) : null);
}
public boolean isSelectableProject(FlexoProject project) {
return context.getProjectLoader().getRootProjects().contains(project);
}
/**********************
* NAVIGATION HISTORY *
**********************/
public FlexoModelObject getCurrentObject() {
return currentLocation != null ? currentLocation.getObject() : null;
}
public void setCurrentObject(FlexoModelObject object) {
// Little block to change the currentPerspective if the
// current perspective can't handle this object
FlexoPerspective perspective = getCurrentPerspective();
if (object != null && !perspective.hasModuleViewForObject(object)) {
for (FlexoPerspective p : getPerspectives()) {
if (p == null) {
continue;
}
if (p.hasModuleViewForObject(object)) {
perspective = p;
break;
}
}
}
setCurrentLocation(getCurrentEditor(), object, perspective);
}
public Location getCurrentLocation() {
return currentLocation;
}
public void setCurrentLocation(Location location) {
if (location != null && location.equals(getCurrentLocation())) {
return;
} else if (location != null) {
setCurrentLocation(location.getEditor(), location.getObject(), location.getPerspective());
}
}
public Set<Location> getLocations() {
return Collections.unmodifiableSet(locations);
}
/**
* Adds the specified location to the set of locations if it is not yet in the set. The method returns a location which is equal to the
* specified location and which is in the set (ie, if the location was not yet in the set, it returns the specified location, else it
* returns the locations that was previously contained).
*
* @param location
* the location to add.
* @return the location which is contained in the set.
*/
public Location addToLocations(Location location) {
Location existing = locations.get(location);
if (existing == null) {
locations.add(existing = location);
getPropertyChangeSupport().firePropertyChange(LOCATIONS, null, existing = location);
}
return existing;
}
public boolean removeFromLocations(Location location) {
boolean removed = locations.remove(location);
if (removed) {
if (location != null && location.equals(currentLocation)) {
setCurrentLocation(getLastLocationForEditor(getCurrentEditor(), getCurrentPerspective()));
}
getPropertyChangeSupport().firePropertyChange(LOCATIONS, location, null);
}
return removed;
}
public void setCurrentLocation(FlexoEditor editor, FlexoModelObject object, FlexoPerspective perspective) {
if (isDeleted()) {
return;
}
if (editor == null) {
editor = getCurrentEditor();
}
if (perspective == null) {
perspective = getCurrentPerspective();
}
if (!isGoingForward && !isGoingBackward) {
if (currentLocation != null && currentLocation != NO_LOCATION) {
previousHistory.push(currentLocation);
}
nextHistory.clear();
}
if (object != null) {
if (getCurrentObject() != object) {
if (!objects.contains(object)) {
registrationManager.new PropertyChangeListenerRegistration(object.getDeletedProperty(), this, object);
objects.add(object);
}
}
}
Location old = currentLocation;
currentLocation = addToLocations(new Location(editor, object, perspective));
notifyLocationChange(old, currentLocation);
}
private void notifyLocationChange(Location old, Location newLocation) {
if (old == null || old.getEditor() != currentLocation.getEditor()) {
getPropertyChangeSupport()
.firePropertyChange(CURRENT_EDITOR, old != null ? old.getEditor() : null, currentLocation.getEditor());
}
if (old == null || old.getPerspective() != currentLocation.getPerspective()) {
getPropertyChangeSupport().firePropertyChange(CURRENT_PERSPECTIVE, old != null ? old.getPerspective() : null,
currentLocation.getPerspective());
}
if (old == null || old.getObject() != currentLocation.getObject()) {
getPropertyChangeSupport()
.firePropertyChange(CURRENT_OBJECT, old != null ? old.getObject() : null, currentLocation.getObject());
}
getPropertyChangeSupport().firePropertyChange(CURRENT_LOCATION, old, currentLocation);
}
public Stack<Location> getNextHistory() {
return nextHistory;
}
public Stack<Location> getPreviousHistory() {
return previousHistory;
}
public boolean canGoForward() {
return nextHistory.size() > 0;
}
public boolean canGoBack() {
return previousHistory.size() > 0;
}
public boolean canGoUp() {
return getCurrentObject() != null && getParent(getCurrentObject()) != null;
}
private Location getLastLocationForEditor(FlexoEditor editor, FlexoPerspective perspective) {
if (perspective != null) {
for (int i = previousHistory.size() - 1; i > -1; i--) {
Location location = previousHistory.get(i);
if ((editor == null || location.getEditor() == editor) && location.getPerspective() == perspective
&& locations.contains(location)) {
return location;
}
}
for (Location location : nextHistory) {
if ((editor == null || location.getEditor() == editor) && location.getPerspective() == perspective
&& locations.contains(location)) {
return location;
}
}
}
for (int i = previousHistory.size() - 1; i > -1; i--) {
Location location = previousHistory.get(i);
if (editor == null || location.getEditor() == editor && locations.contains(location)) {
return location;
}
}
for (Location location : nextHistory) {
if (editor == null || location.getEditor() == editor && locations.contains(location)) {
return location;
}
}
return NO_LOCATION;
}
private FlexoModelObject getParent(FlexoModelObject object) {
if (object instanceof DMObject) {
return ((DMObject) object).getParent();
} else if (object instanceof FlexoProcess) {
return ((FlexoProcess) object).getParentProcess();
} else if (object instanceof CGFile) {
return ((CGFile) object).getRepository();
} else if (object instanceof TOCEntry) {
if (((TOCEntry) object).getParent() != null) {
return ((TOCEntry) object).getParent();
} else {
((TOCEntry) object).getRepository();
}
} else if (object instanceof WSObject) {
return ((WSObject) object).getParent();
} else if (object instanceof ServiceInterface) {
WSPortType proc = ((ServiceInterface) object).getProject().getFlexoWSLibrary()
.getWSPortTypeNamed(((ServiceInterface) object).getName());
if (proc != null) {
return proc.getWSService().getWSPortTypeFolder();
}
} else if (object instanceof ServiceOperation) {
return ((ServiceOperation) object).getServiceInterface();
} else if (object instanceof ServiceMessageDefinition) {
return ((ServiceMessageDefinition) object).getOperation();
} else if (object instanceof MessageEntry) {
return ((MessageEntry) object).getMessage();
}
return null;
}
public void historyBack() {
if (canGoBack()) {
isGoingBackward = true;
try {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Back to " + previousHistory.peek());
}
nextHistory.push(currentLocation);
Location nextLocation = previousHistory.pop();
setCurrentLocation(nextLocation);
} finally {
isGoingBackward = false;
}
}
}
public void historyForward() {
if (canGoForward()) {
isGoingForward = true;
try {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Forward to " + nextHistory.peek());
}
previousHistory.push(currentLocation);
Location nextLocation = nextHistory.pop();
setCurrentLocation(nextLocation);
} finally {
isGoingForward = false;
}
}
}
public void goUp() {
if (canGoUp()) {
setCurrentObject(getParent(getCurrentObject()));
}
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() == getProjectLoader()) {
if (evt.getPropertyName().equals(ProjectLoader.PROJECT_OPENED)) {
FlexoProject project = (FlexoProject) evt.getNewValue();
if (getCurrentPerspective() != null) {
FlexoModelObject object = getCurrentPerspective().getDefaultObject(project);
setCurrentLocation(getProjectLoader().getEditorForProject(project), object, getCurrentPerspective());
} else {
setCurrentProject(project);
}
} else if (evt.getPropertyName().equals(ProjectLoader.PROJECT_CLOSED)) {
handleProjectRemoval((FlexoProject) evt.getOldValue());
setCurrentProject(null);
}
} else if (evt.getOldValue() instanceof FlexoModelObject
&& evt.getPropertyName().equals(((FlexoModelObject) evt.getOldValue()).getDeletedProperty())) {
handleObjectDeletion((FlexoModelObject) evt.getOldValue());
}
}
private void handleProjectRemoval(FlexoProject removedProject) {
updateHistoryForProjectRemoval(previousHistory, removedProject);
updateHistoryForProjectRemoval(nextHistory, removedProject);
for (Location location : new ArrayList<Location>(locations)) {
if (location.getEditor() != null && location.getEditor().getProject() == removedProject) {
removeFromLocations(location);
}
}
}
private void updateHistoryForProjectRemoval(Stack<Location> history, FlexoProject removedProject) {
Iterator<Location> i = history.iterator();
while (i.hasNext()) {
Location hl = i.next();
if (hl.getEditor().getProject() == removedProject) {
i.remove();
}
}
}
private void handleObjectDeletion(FlexoModelObject deletedObject) {
while (objects.remove(deletedObject)) {
;
}
updateHistoryForDeletedObject(previousHistory, deletedObject);
updateHistoryForDeletedObject(nextHistory, deletedObject);
for (Location location : new ArrayList<Location>(locations)) {
if (location.getObject() == deletedObject) {
removeFromLocations(location);
}
}
if (currentLocation != null && currentLocation.getObject() == deletedObject) {
if (canGoBack()) {
historyBack();
} else {
setCurrentObject(null);
}
}
registrationManager.removeListener(deletedObject.getDeletedProperty(), this, deletedObject);
}
private void updateHistoryForDeletedObject(Stack<Location> history, FlexoModelObject deletedObject) {
Iterator<Location> i = history.iterator();
while (i.hasNext()) {
Location hl = i.next();
if (hl.getObject() == deletedObject) {
i.remove();
}
}
}
/**********
* LAYOUT *
**********/
public Node getLayoutForPerspective(FlexoPerspective perspective) {
String layout = GeneralPreferences.getLayoutFor(getModule().getShortName() + perspective.getName());
if (layout != null) {
return getLayoutFromString(layout);
} else {
return null;
}
}
private Node getLayoutFromString(String layout) {
return getGsonLayout().fromJson(layout, Node.class);
}
public void setLayoutForPerspective(FlexoPerspective perspective, Node layout) {
GeneralPreferences.setLayoutFor(getGsonLayout().toJson(layout), getModule().getShortName() + perspective.getName());
GeneralPreferences.save();
}
private Gson getGsonLayout() {
if (gsonLayout == null) {
GsonBuilder builder = new GsonBuilder().registerTypeAdapterFactory(new MultiSplitLayoutTypeAdapterFactory());
gsonLayout = builder.create();
}
return gsonLayout;
}
/**************
* VISIBILITY *
**************/
public boolean isLeftViewVisible() {
return leftViewVisible;
}
public void setLeftViewVisible(boolean leftViewVisible) {
this.leftViewVisible = leftViewVisible;
GeneralPreferences.setShowLeftView(getModule().getShortName(), leftViewVisible);
FlexoPreferences.savePreferences(true);
getPropertyChangeSupport().firePropertyChange(LEFT_VIEW_VISIBLE, !leftViewVisible, leftViewVisible);
}
public boolean isRightViewVisible() {
return rightViewVisible;
}
public void setRightViewVisible(boolean rightViewVisible) {
this.rightViewVisible = rightViewVisible;
GeneralPreferences.setShowRightView(getModule().getShortName(), rightViewVisible);
FlexoPreferences.savePreferences(true);
getPropertyChangeSupport().firePropertyChange(RIGHT_VIEW_VISIBLE, !rightViewVisible, rightViewVisible);
}
}