/** * Copyright (c) 2002-2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation */ package org.eclipse.emf.mapping.command; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.command.CommandParameter; import org.eclipse.emf.edit.command.DragAndDropCommand; import org.eclipse.emf.mapping.Mapping; import org.eclipse.emf.mapping.MappingRoot; import org.eclipse.emf.mapping.domain.MappingDomain; /** * This implementation of a command stack records the command class and command parameter used to create each command. * This information can be used to create and execute all the commands again in a different session. */ public class PersistentCommandStack extends BasicCommandStack { /** * This keeps track of the mapping between commands and their {@link CommandCreationRecord}s. */ protected HashMap<Command, CommandCreationRecord> commandCreationMap = new HashMap<Command, CommandCreationRecord>(); protected MappingDomain domain; protected ClassLoader classLoader; protected String encoding; /** * This constructs and instance of a command stack that records commands using a persistent encoding. */ public PersistentCommandStack(ClassLoader classLoader) { super(); this.classLoader = classLoader; } /** * This is called by the mapping domain whenever a command (that may subsequently be executed) is created. */ public void handleCreateCommand(Class<? extends Command> commandClass, CommandParameter commandParameter, Command command) { // Just remember it; it's encoded later during execution, which is more efficient. // CommandCreationRecord commandCreationRecord = new CommandCreationRecord(commandClass, commandParameter); commandCreationMap.put(command, commandCreationRecord); } /** * You can overide this to create your own type of encoder. */ protected Encoder createEncoder() { return new Encoder(); } /** * You can overide this to create your own type of decoder. */ protected Decoder createDecoder(MappingRoot mappingRoot, ResourceSet resourceSet, ClassLoader classLoader) { return new Decoder(mappingRoot, resourceSet, classLoader); } /** * This override of execute calls {@link CommandCreationRecord#encode} just before normal execution by super. */ @Override public void execute(Command command) { CommandCreationRecord commandCreationRecord = commandCreationMap.get(command); if (commandCreationRecord != null) { // Do the encoding. // commandCreationRecord.encode(createEncoder()); System.out.println("Executing Encoded Command: " + commandCreationRecord.getEncoding()); } else { System.out.println("Executing Unregistered Command: " + command); Thread.dumpStack(); } super.execute(command); if (encoding != null) { executeEncoding(); } } public String getEncoding() { // Record the records for the executed commands on the stack. // Collection<CommandCreationRecord> commandCreationRecordList = new ArrayList<CommandCreationRecord>(); for (int i = 0; i <= top; ++i) { CommandCreationRecord commandCreationRecord = commandCreationMap.get(commandList.get(i)); if (commandCreationRecord == null) { System.out.println("UnregisteredCommand:" + commandList.get(i)); break; } else if (commandCreationRecord.getCommandClass() != RestoreInitialStateCommand.class) { commandCreationRecordList.add(commandCreationRecord); } } Encoder encoder = createEncoder(); encoder.encode(commandCreationRecordList); return encoder.toString(); } public void setEncoding(MappingDomain domain, String encoding) { this.domain = domain; this.encoding = encoding; } protected void executeEncoding() { Decoder decoder = createDecoder(domain.getMappingRoot(), domain.getResourceSet(), classLoader); decoder.setEncoding(encoding); encoding = null; @SuppressWarnings("unchecked") Collection<CommandCreationRecord> commandCreationRecordList = (Collection<CommandCreationRecord>)decoder.decode(); if (commandCreationRecordList != null) { boolean failure = false; for (CommandCreationRecord commandCreationRecord : commandCreationRecordList) { commandCreationRecord.decode(decoder); Command command =domain.createCommand(commandCreationRecord.getCommandClass(), commandCreationRecord.getCommandParameter()); if (command.canExecute()) { System.out.println("Re-executed Command: " + command); execute(command); } else { System.out.println("Not! Executing Command: " + command); command.dispose(); failure = true; break; } } if (!failure) { saveIsDone(); } } } public static class Encoder { protected StringBuffer buffer; public Encoder() { this.buffer = new StringBuffer(); } public Encoder(StringBuffer buffer) { this.buffer = buffer; } public void setBuffer(StringBuffer buffer) { this.buffer = buffer; } public void encode(int value) { buffer.append("<int value=\"" + value + "\"/>"); } public void encode(float value) { buffer.append("<float value=\"" + value + "\"/>"); } public void encode(Object object) { if (object == null) { buffer.append("<null/>"); } else if (object instanceof Class<?>) { @SuppressWarnings("unchecked") Class<? extends Command> theClass = (Class<? extends Command>)object; buffer.append("<class name=\"" + theClass.getName() + "\"/>"); } else if (object instanceof CommandParameter) { CommandParameter commandParameter = (CommandParameter)object; buffer.append("<command-parameter>"); encode(commandParameter.getOwner()); encode(commandParameter.getFeature()); encode(commandParameter.getCollection()); encode(commandParameter.getValue()); encode(commandParameter.getIndex()); buffer.append("</command-parameter>"); } else if (object instanceof EObject) { if (object instanceof Mapping) { Mapping mapping = (Mapping)object; MappingRoot mappingRoot = mapping.getMappingRoot(); if (mappingRoot != null) { Collection<?> mappedObjects = mapping.getMappedObjects(); Collection<?> collection = mappingRoot.getExactMappings(mappedObjects); // If there is more than one exact match, we must get an index number; // int index = 0; if (collection.size() > 1) { // Iterate over the whole tree to do this. // for (TreeIterator<?> mappings = mappingRoot.treeIterator(); mappings.hasNext(); ) { Object otherMapping = mappings.next(); if (otherMapping == mapping) { break; } else if (collection.contains(otherMapping)) { ++index; } } } buffer.append("<mapping>"); encode(mappedObjects); encode(index); buffer.append("</mapping>"); } else { buffer.append("<null/>"); } } else { EObject refObject = (EObject)object; //if (resource != null) { //Resource resource = EcoreUtil.getURI(refObject).; FIX::: String href = EcoreUtil.getURI(refObject).toString(); buffer.append("<ref-object href=\"" + href + "\"/>"); } /*else if (refObject.refPackage() != null && refObject instanceof EStructuralFeature) { EPackage refPackage = refObject.refPackage(); buffer.append("<ref_structural-feature "); buffer.append("package-name=\"" + refPackage.refNamespaceURI() + "\" "); buffer.append("meta-object-name=\"" + refObject.refContainer().refName() + "\" "); buffer.append("meta-feature-name=\"" + refObject.refName() + "\""); buffer.append("/>"); } else { buffer.append("<null/>"); }*/ } } else if (object instanceof Collection<?>) { Collection<?> collection = (Collection<?>)object; buffer.append("<collection>"); for (Object member : collection) { encode(member); } buffer.append("</collection>"); } else if (object instanceof String) { buffer.append("<string value=\"" + (String)object + "\"/>"); } else if (object instanceof DragAndDropCommand.Detail) { DragAndDropCommand.Detail detail = (DragAndDropCommand.Detail)object; buffer.append("<drag-and-drop-detail>"); encode(detail.location); encode(detail.operations); encode(detail.operation); buffer.append("</drag-and-drop-detail>"); } else if (object instanceof CommandCreationRecord) { CommandCreationRecord commandCreationRecord = (CommandCreationRecord)object; buffer.append("<command-creation-record>"); buffer.append(commandCreationRecord.getEncoding()); buffer.append("</command-creation-record>"); } else { buffer.append("<unknown>" + object.toString() + "</unknown>"); } } @Override public String toString() { return buffer.toString(); } } public static class Decoder { protected String string; protected int index = 0; protected ResourceSet resourceSet; protected ClassLoader classLoader; protected MappingRoot mappingRoot; public Decoder(MappingRoot mappingRoot, ResourceSet resourceSet, ClassLoader classLoader) { this.mappingRoot = mappingRoot; this.resourceSet = resourceSet; this.classLoader = classLoader; } public void setEncoding(String encoding) { string = encoding; index = 0; } protected void skipWhitespace() { while (index < string.length() && Character.isWhitespace(string.charAt(index))) { ++index; } } public Object decode() { Object result = null; skipWhitespace(); if (index < string.length() && string.charAt(index) == '<') { ++index; skipWhitespace(); int keyStartIndex = index; while (index < string.length() && !Character.isWhitespace(string.charAt(index)) && string.charAt(index) != '/' && string.charAt(index) != '>') { ++index; } String key = string.substring(keyStartIndex, index); if (key.equals("null")) { index = string.indexOf(">", index) + 1; } else if (key.equals("class")) { index = string.indexOf("\"", index); int classIndex = ++index; index = string.indexOf("\"", index); String className = string.substring(classIndex, index); index = string.indexOf(">", index) + 1; try { result = classLoader.loadClass(className); } catch (Exception exception) { exception.printStackTrace(); } } else if (key.equals("drag-and-drop-detail")) { index = string.indexOf(">", index) + 1; float location = ((Float)decode()).floatValue(); int operations = (Integer)decode(); int operation = (Integer)decode(); index = string.indexOf(">", index) + 1; result = new DragAndDropCommand.Detail(location, operations, operation); } else if (key.equals("command-parameter")) { index = string.indexOf(">", index) + 1; Object owner = decode(); Object feature = decode(); Collection<?> collection = (Collection<?>)decode(); Object value = decode(); int theIndex = (Integer)decode(); index = string.indexOf(">", index) + 1; if (collection == null) { result = new CommandParameter(owner, feature, value, theIndex); } else { result = new CommandParameter(owner, feature, collection, theIndex); } } else if (key.equals("command-creation-record")) { index = string.indexOf(">", index) + 1; int startIndex = index; index = string.indexOf("</command-creation-record>", index); index = string.indexOf(">", index) + 1; result = new CommandCreationRecord(string.substring(startIndex, index)); } else if (key.equals("ref-object")) { index = string.indexOf("\"", index); int hrefStartIndex = ++index; index = string.indexOf("\"", index); String href = string.substring(hrefStartIndex, index); index = string.indexOf(">", index) + 1; result = resourceSet.getEObject(URI.createURI(href), true); } else if (key.equals("ref_structural-feature")) { index = string.indexOf("\"", index); int startIndex = ++index; index = string.indexOf("\"", index); String packageURI = string.substring(startIndex, index); index = string.indexOf("\"", index + 1); startIndex = ++index; index = string.indexOf("\"", index); String metaObjectName = string.substring(startIndex, index); index = string.indexOf("\"", index + 1); startIndex = ++index; index = string.indexOf("\"", index); String metaFeatureName = string.substring(startIndex, index); index = string.indexOf(">", index) + 1; EPackage refPackage = EPackage.Registry.INSTANCE.getEPackage(packageURI); EClass metaObject = (EClass)refPackage.getEClassifier(metaObjectName); EObject metaFeature = metaObject.getEStructuralFeature(metaFeatureName); result = metaFeature; } else if (key.equals("mapping")) { index = string.indexOf(">", index) + 1; Collection<?> mappedObjects = (Collection<?>)decode(); int value = (Integer)decode(); index = string.indexOf(">", index) + 1; Collection<?> collection = mappingRoot.getExactMappings(mappedObjects); if (collection.size() > 1) { // Iterate over the whole tree to do this. // for (TreeIterator<?> mappings = mappingRoot.treeIterator(); mappings.hasNext(); ) { Object mapping = mappings.next(); if (collection.contains(mapping)) { if (value == 0) { result = mapping; break; } else { --value; } } } } if (result == null && !collection.isEmpty()) { result = collection.iterator().next(); } } else if (key.equals("collection")) { Collection<Object> collection = new ArrayList<Object>(); index = string.indexOf(">", index) + 1; while (index < string.length() && Character.isWhitespace(string.charAt(index))) { ++index; } while (index < string.length() && string.indexOf("</collection>", index) != index) { Object object = decode(); collection.add(object); while (index < string.length() && Character.isWhitespace(string.charAt(index))) { ++index; } } if (index < string.length()) { index += "</collection>".length(); } result = collection; } else if (key.equals("string")) { index = string.indexOf("\"", index); int startIndex = ++index; index = string.indexOf("\"", index); String value = string.substring(startIndex, index); index = string.indexOf(">", index) + 1; result = value; } else if (key.equals("int")) { index = string.indexOf("\"", index); int startIndex = ++index; index = string.indexOf("\"", index); String value = string.substring(startIndex, index); index = string.indexOf(">", index) + 1; result = Integer.valueOf(value); } else if (key.equals("float")) { index = string.indexOf("\"", index); int startIndex = ++index; index = string.indexOf("\"", index); String value = string.substring(startIndex, index); index = string.indexOf(">", index) + 1; result = Float.valueOf(value); } else { index = string.indexOf("</unknown>", index) + "</unknown>".length(); } // System.out.println("Decoded key= '" + key + "':'" + string.substring(resultStartIndex, index) + "'"); skipWhitespace(); } return result; } @Override public String toString() { return index < string.length() ? string.substring(index) : ""; } } /** * This records and encodes the command class and command parameter. */ public static class CommandCreationRecord { Class<? extends Command> commandClass; CommandParameter commandParameter; String encoding; public CommandCreationRecord(Class<? extends Command> commandClass, CommandParameter commandParameter) { this.commandClass = commandClass; this.commandParameter = commandParameter; this.encoding = null; } public CommandCreationRecord(String encoding) { this.encoding = encoding; } public CommandParameter getCommandParameter() { return commandParameter; } public String getEncoding() { return encoding; } public Class<? extends Command> getCommandClass() { return commandClass; } public void encode(Encoder encoder) { encoder.encode(commandClass); encoder.encode(commandParameter); encoding = encoder.toString(); } @SuppressWarnings("unchecked") public void decode(Decoder decoder) { decoder.setEncoding(encoding); commandClass = (Class<? extends Command>)decoder.decode(); commandParameter = (CommandParameter)decoder.decode(); } @Override public String toString() { return "CommandCreationRecord { commandClass=" + (commandClass == null ? "null" : commandClass.getName()) + ", commandParameter=" + commandParameter + " }"; } } }