/*
* (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.foundation.action;
import java.util.Vector;
import java.util.logging.Logger;
import org.openflexo.foundation.FlexoEditor;
import org.openflexo.foundation.FlexoException;
import org.openflexo.foundation.FlexoModelObject;
import org.openflexo.foundation.utils.FlexoProgress;
import org.openflexo.foundation.utils.FlexoProgressFactory;
import org.openflexo.kvc.KeyValueCoding;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.xmlcode.KeyValueCoder;
import org.openflexo.xmlcode.KeyValueDecoder;
/**
* Abstract representation of an action on Flexo model (model edition primitive)
*
* T2 is arbitrary and should be removed in the long run. There is absolutely no guarantee on the actual type of T2. No assertions can be
* made. T1 can be kept if we ensure that only actions of the type FlexoAction<A extends FlexoAction<A, T1>, T1 extends FlexoModelObject>
* are actually returned for a given object of type T1.
*
* @author sguerin
*/
public abstract class FlexoAction<A extends FlexoAction<A, T1, T2>, T1 extends FlexoModelObject, T2 extends FlexoModelObject> implements
KeyValueCoding {
private static final Logger logger = FlexoLogger.getLogger(FlexoAction.class.getPackage().getName());
private FlexoActionType<A, T1, T2> _actionType;
private T1 _focusedObject;
private Vector<T2> _globalSelection;
private Object _context;
private Object _invoker;
private FlexoProgress _flexoProgress;
private FlexoEditor _editor;
private ExecutionContext _executionContext;
public void delete() {
if (_executionContext != null) {
_executionContext.delete();
}
_editor = null;
_flexoProgress = null;
_invoker = null;
_context = null;
if (_globalSelection != null) {
_globalSelection.clear();
}
_globalSelection = null;
_focusedObject = null;
_actionType = null;
}
public enum ExecutionStatus {
NEVER_EXECUTED,
EXECUTING_CORE,
HAS_SUCCESSFULLY_EXECUTED,
FAILED_EXECUTION,
EXECUTING_UNDO_CORE,
HAS_SUCCESSFULLY_UNDONE,
FAILED_UNDO_EXECUTION,
EXECUTING_REDO_CORE,
HAS_SUCCESSFULLY_REDONE,
FAILED_REDO_EXECUTION;
public boolean hasActionExecutionSucceeded() {
return this == ExecutionStatus.HAS_SUCCESSFULLY_EXECUTED;
}
public boolean hasActionUndoExecutionSucceeded() {
return this == HAS_SUCCESSFULLY_UNDONE;
}
public boolean hasActionRedoExecutionSucceeded() {
return this == HAS_SUCCESSFULLY_REDONE;
}
}
/*private boolean _isExecutingInitializer = false;
private boolean _isExecutingFinalizer = false;
private boolean _isExecutingCore = false;
private boolean _isExecuting = false;*/
// private boolean actionExecutionSucceeded = false;
protected ExecutionStatus executionStatus = ExecutionStatus.NEVER_EXECUTED;
private FlexoException thrownException = null;
public FlexoAction(FlexoActionType<A, T1, T2> actionType, T1 focusedObject, Vector<T2> globalSelection, FlexoEditor editor) {
super();
_editor = editor;
_actionType = actionType;
_focusedObject = focusedObject;
if (globalSelection != null) {
_globalSelection = new Vector<T2>();
for (T2 o : globalSelection) {
_globalSelection.add(o);
}
} else {
_globalSelection = null;
}
}
public FlexoActionType<A, T1, T2> getActionType() {
return _actionType;
}
public String getLocalizedName() {
if (getActionType() != null) {
return getActionType().getLocalizedName();
}
return null;
}
public String getLocalizedDescription() {
if (getActionType() != null) {
return getActionType().getLocalizedDescription();
}
return null;
}
/**
* Sets focused object
*/
public void setFocusedObject(T1 focusedObject) {
_focusedObject = focusedObject;
}
/**
* Return focused object, according to the one used in action constructor (see FlexoAction factory). If no focused object was defined,
* and global selection is not empty, return first object in the selection
*
* @return a FlexoModelObject instance, representing focused object
*/
public T1 getFocusedObject() {
if (_focusedObject != null) {
return _focusedObject;
}
if (_globalSelection != null && _globalSelection.size() > 0) {
return (T1) _globalSelection.firstElement();
}
return null;
}
public Vector<T2> getGlobalSelection() {
return _globalSelection;
}
public A doAction() {
if (_editor != null) {
_editor.performAction((A) this, null);
} else {
try {
doActionInContext();
} catch (FlexoException e) {
e.printStackTrace();
}
}
return (A) this;
}
public ExecutionStatus getExecutionStatus() {
return executionStatus;
}
public boolean hasActionExecutionSucceeded() {
return getExecutionStatus().hasActionExecutionSucceeded();
}
public FlexoException getThrownException() {
return thrownException;
}
public boolean isLongRunningAction() {
return false;
}
public A doActionInContext() throws FlexoException {
if (!getActionType().isEnabled(getFocusedObject(), getGlobalSelection())) {
throw new InactiveFlexoActionException(getActionType(), getFocusedObject(), getGlobalSelection());
}
try {
executionStatus = ExecutionStatus.EXECUTING_CORE;
doAction(getContext());
executionStatus = ExecutionStatus.HAS_SUCCESSFULLY_EXECUTED;
} catch (FlexoException e) {
executionStatus = ExecutionStatus.FAILED_EXECUTION;
thrownException = e;
throw e;
}
return (A) this;
}
public void hideFlexoProgress() {
if (getFlexoProgress() != null) {
getFlexoProgress().hideWindow();
setFlexoProgress(null);
}
}
protected abstract void doAction(Object context) throws FlexoException;
public Object getContext() {
return _context;
}
public void setContext(Object context) {
_context = context;
}
public Object getInvoker() {
return _invoker;
}
public void setInvoker(Object invoker) {
_invoker = invoker;
}
public FlexoProgressFactory getFlexoProgressFactory() {
if (getEditor() != null) {
return getEditor().getFlexoProgressFactory();
}
return null;
}
public FlexoProgress getFlexoProgress() {
return _flexoProgress;
}
public void setFlexoProgress(FlexoProgress flexoProgress) {
_flexoProgress = flexoProgress;
}
public FlexoProgress makeFlexoProgress(String title, int steps) {
if (getFlexoProgressFactory() != null) {
setFlexoProgress(getFlexoProgressFactory().makeFlexoProgress(title, steps));
return getFlexoProgress();
}
return null;
}
public void setProgress(String stepName) {
if (getFlexoProgress() != null) {
getFlexoProgress().setProgress(stepName);
}
}
public void resetSecondaryProgress(int steps) {
if (getFlexoProgress() != null) {
getFlexoProgress().resetSecondaryProgress(steps);
}
}
public void setSecondaryProgress(String stepName) {
if (getFlexoProgress() != null) {
getFlexoProgress().setSecondaryProgress(stepName);
}
}
public Vector<FlexoModelObject> getGlobalSelectionAndFocusedObject() {
return getGlobalSelectionAndFocusedObject(getFocusedObject(), getGlobalSelection());
}
public static Vector<FlexoModelObject> getGlobalSelectionAndFocusedObject(FlexoModelObject focusedObject,
Vector<? extends FlexoModelObject> globalSelection) {
Vector<FlexoModelObject> v = globalSelection != null ? new Vector<FlexoModelObject>(globalSelection.size() + 1)
: new Vector<FlexoModelObject>(1);
if (globalSelection != null) {
v.addAll(globalSelection);
}
if (focusedObject != null && !v.contains(focusedObject)) {
v.add(focusedObject);
}
return v;
}
public FlexoEditor getEditor() {
return _editor;
}
private FlexoAction<?, ?, ?> ownerAction;
public FlexoAction<?, ?, ?> getOwnerAction() {
return ownerAction;
}
protected void setOwnerAction(FlexoAction<?, ?, ?> ownerAction) {
this.ownerAction = ownerAction;
}
public boolean isEmbedded() {
return getOwnerAction() != null;
}
public String toSimpleString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
}
@Override
public String toString() {
boolean isFirst = true;
StringBuilder returned = new StringBuilder();
if (getExecutionContext() != null) {
for (String key : getExecutionContext().getObjectsCreatedWhileExecutingAction().keySet()) {
FlexoModelObject o = getExecutionContext().getObjectsCreatedWhileExecutingAction().get(key);
returned.append(isFirst ? "" : " ").append("CREATED:").append(key).append("/").append(o.getClass().getSimpleName())
.append("/").append(o.getFlexoID());
isFirst = false;
}
for (String key : getExecutionContext().getObjectsDeletedWhileExecutingAction().keySet()) {
FlexoModelObject o = getExecutionContext().getObjectsDeletedWhileExecutingAction().get(key);
returned.append(isFirst ? "" : " ").append("DELETED:").append(key).append("/").append(o.getClass().getSimpleName())
.append("/").append(o.getFlexoID());
isFirst = false;
}
}
returned.append("]");
return returned.toString();
}
public ExecutionContext getExecutionContext() {
// If not supplied, create default ExecutionContext
if (_executionContext == null) {
_executionContext = createDefaultExecutionContext();
}
return _executionContext;
}
public void setExecutionContext(ExecutionContext executionContext) {
_executionContext = executionContext;
}
protected ExecutionContext createDefaultExecutionContext() {
return new ExecutionContext(this);
}
public void objectCreated(String key, FlexoModelObject object) {
getExecutionContext().objectCreated(key, object);
}
public void objectDeleted(String key, FlexoModelObject object) {
if (getExecutionContext() != null) {
getExecutionContext().objectDeleted(key, object);
}
}
/**
* Hook that might be overriden in sub-classes while implementing dynamic reference replacement scheme
*
* @param propertyKey
* @param newValue
* @param oldValue
* TODO
* @param originalValue
* TODO
*/
protected void replacedSinglePropertyValue(String propertyKey, Object newValue, Object oldValue, Object originalValue) {
}
/**
* Hook that might be overriden in sub-classes while implementing dynamic reference replacement scheme
*
* @param propertyKey
* @param index
* @param newValue
* @param oldValue
* TODO
* @param originalValue
* TODO
*/
protected void replacedVectorPropertyValue(String propertyKey, int index, Object newValue, Object oldValue, Object originalValue) {
}
// ==============================================================
// ============= Key/Value coding implementation ================
// ==============================================================
@Override
public String valueForKey(String key) {
if (_editor != null) {
return KeyValueDecoder.valueForKey(this, key, _editor.getProject().getStringEncoder());
} else {
return KeyValueDecoder.valueForKey(this, key);
}
}
@Override
public void setValueForKey(String valueAsString, String key) {
if (_editor != null) {
KeyValueCoder.setValueForKey(this, valueAsString, key, _editor.getProject().getStringEncoder());
} else {
KeyValueCoder.setValueForKey(this, valueAsString, key);
}
}
@Override
public boolean booleanValueForKey(String key) {
return KeyValueDecoder.booleanValueForKey(this, key);
}
@Override
public byte byteValueForKey(String key) {
return KeyValueDecoder.byteValueForKey(this, key);
}
@Override
public char characterForKey(String key) {
return KeyValueDecoder.characterValueForKey(this, key);
}
@Override
public double doubleValueForKey(String key) {
return KeyValueDecoder.doubleValueForKey(this, key);
}
@Override
public float floatValueForKey(String key) {
return KeyValueDecoder.floatValueForKey(this, key);
}
@Override
public int integerValueForKey(String key) {
return KeyValueDecoder.integerValueForKey(this, key);
}
@Override
public long longValueForKey(String key) {
return KeyValueDecoder.longValueForKey(this, key);
}
@Override
public short shortValueForKey(String key) {
return KeyValueDecoder.shortValueForKey(this, key);
}
@Override
public void setBooleanValueForKey(boolean value, String key) {
KeyValueCoder.setBooleanValueForKey(this, value, key);
}
@Override
public void setByteValueForKey(byte value, String key) {
KeyValueCoder.setByteValueForKey(this, value, key);
}
@Override
public void setCharacterForKey(char value, String key) {
KeyValueCoder.setCharacterValueForKey(this, value, key);
}
@Override
public void setDoubleValueForKey(double value, String key) {
KeyValueCoder.setDoubleValueForKey(this, value, key);
}
@Override
public void setFloatValueForKey(float value, String key) {
KeyValueCoder.setFloatValueForKey(this, value, key);
}
@Override
public void setIntegerValueForKey(int value, String key) {
KeyValueCoder.setIntegerValueForKey(this, value, key);
}
@Override
public void setLongValueForKey(long value, String key) {
KeyValueCoder.setLongValueForKey(this, value, key);
}
@Override
public void setShortValueForKey(short value, String key) {
KeyValueCoder.setShortValueForKey(this, value, key);
}
@Override
public Object objectForKey(String key) {
return KeyValueDecoder.objectForKey(this, key);
}
@Override
public void setObjectForKey(Object value, String key) {
KeyValueCoder.setObjectForKey(this, value, key);
}
// Retrieving type
@Override
public Class<?> getTypeForKey(String key) {
return KeyValueDecoder.getTypeForKey(this, key);
}
@Override
public boolean isSingleProperty(String key) {
return KeyValueDecoder.isSingleProperty(this, key);
}
@Override
public boolean isArrayProperty(String key) {
return KeyValueDecoder.isArrayProperty(this, key);
}
@Override
public boolean isVectorProperty(String key) {
return KeyValueDecoder.isVectorProperty(this, key);
}
@Override
public boolean isHashtableProperty(String key) {
return KeyValueDecoder.isHashtableProperty(this, key);
}
}