/*******************************************************************************
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package hr.fer.zemris.vhdllab.applets.editor.schema2.model.commands;
import hr.fer.zemris.vhdllab.applets.editor.schema2.enums.EConstraintExplanation;
import hr.fer.zemris.vhdllab.applets.editor.schema2.enums.EErrorTypes;
import hr.fer.zemris.vhdllab.applets.editor.schema2.enums.EPropertyChange;
import hr.fer.zemris.vhdllab.applets.editor.schema2.exceptions.InvalidCommandOperationException;
import hr.fer.zemris.vhdllab.applets.editor.schema2.exceptions.ParameterNotFoundException;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ICommand;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ICommandResponse;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.IParameter;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.IParameterCollection;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.IParameterConstraint;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.IParameterEvent;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaComponent;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaInfo;
import hr.fer.zemris.vhdllab.applets.editor.schema2.interfaces.ISchemaWire;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.Caseless;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.ChangeTuple;
import hr.fer.zemris.vhdllab.applets.editor.schema2.misc.SchemaError;
import hr.fer.zemris.vhdllab.applets.editor.schema2.model.CommandResponse;
import java.util.ArrayList;
import java.util.List;
/**
* Mijenja vrijednost parametra.
* U slucaju da constraint onemogucava promjenu vrijednosti,
* razlog nemogucnosti promjene vrijednosti nalazi se u info mapi
* pod kljucem KEY_CONSTRAINT_EXPLANATION u obliku EConstraintExplanation.
* U slucaju uspjesne promjene parametra, u info mapi ce se pod kljucem
* KEY_UPDATED_NAME naci novo ime komponente, odnosno zice.
*
* @author Axel
*
*/
public class SetParameterCommand implements ICommand {
public static enum EParameterHolder {
entity, wire, component
}
protected static class PPWrapper {
public IParameterCollection params;
public IParameter par;
}
/* static fields */
public static final String COMMAND_NAME = "SetParameterCommand";
public static final String KEY_CONSTRAINT_EXPLANATION = "CONSTRAINT_EXPL";
public static final String KEY_UPDATED_NAME = "UPDATED_NAME";
/* private fields */
private Caseless objname;
private String paramname;
private EParameterHolder holder;
private Object val, oldval;
private boolean undoable;
private PPWrapper ppw;
private ISchemaWire tmpwire;
private ISchemaComponent tmpcmp;
/* ctors */
/**
* Konstruira zahtjev za promjenom parametra.
*
* @param objectName
* Ime zice ili komponente kojoj pripada parameter. Ako se radi o
* parametru samog entitya (modeliranog sklopa), ovo moze biti null.
* @param parameterName
* Ime parametra kojem treba promijeniti vrijednost.
* @param parameterHolder
* Enumeracija koja govori o tome kome pripada parametar (komponenti, zici, ...).
* @param value
* Nova vrijednost parametra.
* @param info
* ISchemaInfo objekt koji je ovdje kako bi se vec pri samoj konstrukciji
* zahtjeva moglo utvrditi da li je parametar undoable.
*/
public SetParameterCommand(Caseless objectName, String parameterName,
EParameterHolder parameterHolder, Object value, ISchemaInfo info)
{
ppw = new PPWrapper();
objname = objectName;
paramname = parameterName;
holder = parameterHolder;
val = value;
// determine whether this command is undoable
// on basis of the parameter itself
fetchParameter(info);
IParameter param = ppw.par;
if (param == null) {
undoable = false;
} else {
if (param.getParameterEvent() == null) undoable = true;
else undoable = param.getParameterEvent().isUndoable();
}
}
/* methods */
private EErrorTypes fetchParameters(ISchemaInfo info) {
switch (holder) {
case entity:
ppw.params = info.getEntity().getParameters();
break;
case wire:
ISchemaWire wire = info.getWires().fetchWire(objname);
if (wire == null) return EErrorTypes.NONEXISTING_WIRE_NAME;
ppw.params = wire.getParameters();
tmpwire = wire;
break;
case component:
ISchemaComponent comp = info.getComponents().fetchComponent(objname);
if (comp == null) return EErrorTypes.NONEXISTING_COMPONENT_NAME;
if (comp.isInvalidated()) return EErrorTypes.COMPONENT_INVALIDATED;
ppw.params = comp.getParameters();
tmpcmp = comp;
break;
}
return EErrorTypes.NO_ERROR;
}
private EErrorTypes fetchParameter(ISchemaInfo info) {
IParameterCollection params = null;
EErrorTypes err = fetchParameters(info);
params = ppw.params;
if (params == null) return err;
try {
ppw.par = params.getParameter(paramname);
} catch (ParameterNotFoundException e) {
return EErrorTypes.PARAMETER_NOT_FOUND;
}
return EErrorTypes.NO_ERROR;
}
public String getCommandName() {
return COMMAND_NAME;
}
public boolean isUndoable() {
return undoable;
}
public ICommandResponse performCommand(ISchemaInfo info) {
IParameter param = null;
EErrorTypes err = fetchParameter(info);
param = ppw.par;
if (err.equals(EErrorTypes.PARAMETER_NOT_FOUND) ||
err.equals(EErrorTypes.NONEXISTING_COMPONENT_NAME) ||
err.equals(EErrorTypes.NONEXISTING_WIRE_NAME))
{
return new CommandResponse(new SchemaError(err, "No such wire, component or parameter."));
}
if (err.equals(EErrorTypes.COMPONENT_INVALIDATED))
return new CommandResponse(new SchemaError(EErrorTypes.COMPONENT_INVALIDATED,
"Component that holds the parameter is invalidated."));
// save old value for undo
oldval = param.getValue();
// check constraint
IParameterConstraint constraint = param.getConstraint();
EConstraintExplanation cttresult = constraint.getExplanation(val);
if (cttresult != EConstraintExplanation.ALLOWED) {
CommandResponse response = new CommandResponse(
new SchemaError(EErrorTypes.PARAMETER_CONSTRAINT_BAN,
"Parameter value not allowed by constraint."));
response.getInfoMap().set(KEY_CONSTRAINT_EXPLANATION, cttresult);
return response;
}
// change value
param.setValue(val);
// fire event if it exists
IParameterEvent pe = param.getParameterEvent();
List<ChangeTuple> changes = null;
if (pe != null) {
changes = pe.getChanges();
SchemaError error = fireEvent(info, param, pe, oldval);
if (error != null) {
// return old value if something went wrong
param.setValue(oldval);
return new CommandResponse(error);
}
}
// add updated name to info map
Caseless updname = getUpdatedName(info);
if (changes == null) {
CommandResponse cr = new CommandResponse(new ChangeTuple(EPropertyChange.PROPERTY_CHANGE));
cr.getInfoMap().set(KEY_UPDATED_NAME, updname);
return cr;
}
changes = new ArrayList<ChangeTuple>(changes);
changes.add(new ChangeTuple(EPropertyChange.PROPERTY_CHANGE));
CommandResponse cr = new CommandResponse(changes);
cr.getInfoMap().set(KEY_UPDATED_NAME, updname);
return cr;
}
public ICommandResponse undoCommand(ISchemaInfo info) throws InvalidCommandOperationException {
if (!undoable) throw new InvalidCommandOperationException("Parameter event is not undoable.");
IParameter param = null;
fetchParameter(info);
param = ppw.par;
if (param == null) return new CommandResponse(new SchemaError(EErrorTypes.PARAMETER_NOT_FOUND,
"No such wire, component or parameter."));
// check constraint
IParameterConstraint constraint = param.getConstraint();
EConstraintExplanation cttresult = constraint.getExplanation(oldval);
if (cttresult != EConstraintExplanation.ALLOWED) {
CommandResponse response = new CommandResponse(
new SchemaError(EErrorTypes.PARAMETER_CONSTRAINT_BAN,
"Parameter value not allowed by constraint."));
response.getInfoMap().set(KEY_CONSTRAINT_EXPLANATION, cttresult);
return response;
}
// change value
param.setValue(oldval);
// fire event if it exists
IParameterEvent pe = param.getParameterEvent();
List<ChangeTuple> changes = null;
if (pe != null) {
changes = pe.getChanges();
SchemaError error = fireEvent(info, param, pe, val);
if (error != null) {
// return old value if something went wrong
param.setValue(val);
return new CommandResponse(error);
}
}
// add updated name to info map
Caseless updname = getUpdatedName(info);
if (changes == null) {
CommandResponse cr = new CommandResponse(new ChangeTuple(EPropertyChange.PROPERTY_CHANGE));
cr.getInfoMap().set(KEY_UPDATED_NAME, updname);
return cr;
}
changes = new ArrayList<ChangeTuple>(changes);
changes.add(new ChangeTuple(EPropertyChange.PROPERTY_CHANGE));
CommandResponse cr = new CommandResponse(changes);
cr.getInfoMap().set(KEY_UPDATED_NAME, updname);
return cr;
}
private Caseless getUpdatedName(ISchemaInfo info) {
switch (holder) {
case entity:
return info.getEntity().getName();
case wire:
return tmpwire.getName();
case component:
return tmpcmp.getName();
}
throw new IllegalStateException("Holder is enum, but did not end in switch?");
}
private SchemaError fireEvent(ISchemaInfo info, IParameter param, IParameterEvent pe,
Object oldvalue)
{
ISchemaComponent component = null;
ISchemaWire wire = null;
switch (holder) {
case component:
component = info.getComponents().fetchComponent(objname);
if (component == null) return new SchemaError(EErrorTypes.NONEXISTING_COMPONENT_NAME);
break;
case wire:
wire = info.getWires().fetchWire(objname);
if (wire == null) return new SchemaError(EErrorTypes.NONEXISTING_WIRE_NAME);
break;
}
if (!pe.performChange(oldvalue, param, info, wire, component))
return new SchemaError(EErrorTypes.EVENT_NOT_COMPLETED,
"Parameter value could not be changed.");
return null;
}
}