Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [qvto-dev] Fwd: Re: Fwd: Re: Eclipse QVTo: saving QVTo resource

Hi Sergey,

Am 06.03.2016 um 20:11 schrieb Sergey Boyko:

Actually I would put it on your opinion.

Ok, so here you go. You have been warned :)

Current status: Passes all of my 386 testcases.
- Emits comments when expected fields or subelements are missing in the AST
(should maybe made configurable via java properties or similarly)
- pretty printing is minimal, but indentation should mostly work
- only syntactic "sugar": is unwrapping single ObjectExp in population section of mapping
to result in more common way of writing a mapping body

I will now try to replace the EMFCompare usage by your recommended framework asap
and will then add final GIT-patch to my 4 year old bugzilla entry.

Best Regards

Uwe
/*******************************************************************************
 * Copyright (c) 2007, 2009 Borland Software 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:
 *     Borland Software Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.m2m.internal.qvt.oml.runtime.resource;

import java.io.*;
import java.util.*;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.DOMHandler;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.m2m.internal.qvt.oml.QvtMessage;
import org.eclipse.m2m.internal.qvt.oml.common.MdaException;
import org.eclipse.m2m.internal.qvt.oml.compiler.CompiledUnit;
import org.eclipse.m2m.internal.qvt.oml.compiler.QvtCompilerOptions;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.VisitableASTNode;
import org.eclipse.m2m.internal.qvt.oml.runtime.project.QvtModule;
import org.eclipse.m2m.internal.qvt.oml.runtime.project.TransformationUtil;
import org.eclipse.m2m.internal.qvt.oml.runtime.util.Messages;
import org.eclipse.osgi.util.NLS;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

/**
 * @author sboyko
 *
 */
public class QvtOperationalResourceImpl extends XMIResourceImpl {

    public QvtOperationalResourceImpl(URI uri) {
    	super(uri);
    }
	
    @Override
    public void load(Map<?, ?> options) throws IOException {
    	if (!isLoaded()) {
    		Notification notification = null;
    		isLoading = true;

			if (errors != null) {
				errors.clear();
			}
			if (warnings != null) {
				warnings.clear();
			}
    		
            try {
            	URI normalizedUri = getURIConverter().normalize(getURI());
            	QvtModule qvtModule = TransformationUtil.getQvtModule(normalizedUri);

            	QvtCompilerOptions qvtOptions = new QvtCompilerOptions();
            	qvtOptions.setModuleWithErrorAllowed(true);
            	qvtModule.setQvtCompilerOptions(qvtOptions);
            	
            	CompiledUnit unit = qvtModule.getUnit();
				fillCompilationDiagnostic(unit, normalizedUri);

				if (unit.getModules().isEmpty()) {
    				throw new IOException(NLS.bind(Messages.QvtResource_moduleCompilationErrors, 
    						normalizedUri, unit.getProblems()));
    			}

        		notification = setLoaded(true);
    			getContents().addAll(unit.getModules());
            }
            catch (MdaException e) {
				throw new IOWrappedException(e);
			}
            finally {
				isLoading = false;
				
				if (notification != null) {
					eNotify(notification);
				}
				
				setModified(false);
            }
    	}
    }
    
    private void fillCompilationDiagnostic(CompiledUnit unit, URI uri) {
    	warnings = getWarnings();
		for (QvtMessage msg : unit.getWarnings()) {
			warnings.add(new Diagnostic(msg.getMessage(), uri.toString(), msg.getLineNum()));
		}
		for (QvtMessage msg : unit.getErrors()) {
			warnings.add(new QvtCompilationErrorException(msg, uri.toString(), msg.getLineNum()));
		}
    }

	@Override
    public void doLoad(InputStream inputStream, Map<?, ?> options)
    		throws IOException {
    	throw new UnsupportedOperationException();
    }

    @Override
    public void doLoad(InputSource inputSource, Map<?, ?> options) throws IOException {
    	throw new UnsupportedOperationException();
    }

    @Override
    public void doLoad(Node node, Map<?, ?> options) throws IOException {
    	throw new UnsupportedOperationException();
    }
    
    @Override
    public Document save(Document doc, Map<?, ?> options, DOMHandler handler) {
//    	throw new UnsupportedOperationException();
    	System.out.println("save(Document,Map,DOMHandler)");
    	return doc;
    }
    
    @Override
    public void save(Map<?, ?> options) throws IOException {
    	// resource operates as read-only
    	super.save(options);
    }
    
    @Override
    public void doSave(OutputStream outputStream, Map<?,?> options) throws IOException {
		this.doSave(new OutputStreamWriter(outputStream), options);
    }
    
	@Override
	public void doSave(Writer writer, Map<?, ?> options) throws IOException
	{
		UnparsingQVTOVisitor unparsingVisitor = new UnparsingQVTOVisitor();
		
		boolean isFirstModule = true;
		for (EObject eObject : this.getContents())
		{
			Module module = (Module) eObject;
			unparsingVisitor.unparseModule(module,isFirstModule);
			isFirstModule = false;
		}
		
		for (String string : unparsingVisitor.getLines())
		{
			if ( string != null )
			{
				writer.write(string);
				writer.write(System.lineSeparator());
			}
		}
		
		writer.flush();
	}

    @Override
    public boolean isModified() {
    	return false;
    }
    
    @Override
    public boolean isTrackingModification() {
    	return false;
    }
    
	/**
	 * Just a marker for diagnostic notification messages
	 */
	private static class Diagnostic implements Resource.Diagnostic {
		private final String myMessage;
		private final String myLocation;
		private final int myLine;

		Diagnostic(String message, String  location, int line) {
			myMessage = message;
			myLocation = location;
			myLine = line;
		}

		public String getMessage() {
			return myMessage;
		}		

		public String getLocation() {
			return myLocation;
		}

		public int getColumn() {
			return 0;
		}

		public int getLine() {
			return myLine;
		}
	}

}
package org.eclipse.m2m.internal.qvt.oml.runtime.resource;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypeParameter;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtEnvironmentBase;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.IntermediateClassFactory;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil;
import org.eclipse.m2m.internal.qvt.oml.cst.parser.QvtKeywords;
import org.eclipse.m2m.internal.qvt.oml.expressions.Constructor;
import org.eclipse.m2m.internal.qvt.oml.expressions.ConstructorBody;
import org.eclipse.m2m.internal.qvt.oml.expressions.ContextualProperty;
import org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind;
import org.eclipse.m2m.internal.qvt.oml.expressions.EntryOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.Helper;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeCallExp;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImportKind;
import org.eclipse.m2m.internal.qvt.oml.expressions.Library;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingBody;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingCallExp;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingParameter;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModelParameter;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModelType;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModuleImport;
import org.eclipse.m2m.internal.qvt.oml.expressions.ObjectExp;
import org.eclipse.m2m.internal.qvt.oml.expressions.OperationBody;
import org.eclipse.m2m.internal.qvt.oml.expressions.OperationalTransformation;
import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveExp;
import org.eclipse.m2m.internal.qvt.oml.expressions.ResolveInExp;
import org.eclipse.m2m.internal.qvt.oml.expressions.VarParameter;
import org.eclipse.m2m.internal.qvt.oml.expressions.util.QVTOperationalVisitor;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AltExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssertExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.AssignExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BlockExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.BreakExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.CatchExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ComputeExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ContinueExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictLiteralExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictLiteralPart;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.DictionaryType;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ForExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeExpression;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeIterateExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ImperativeLoopExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.InstantiationExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ListLiteralExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ListType;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.LogExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.OrderedTupleLiteralExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.OrderedTupleLiteralPart;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.RaiseExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.ReturnExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.SeverityKind;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.SwitchExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.TryExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.UnlinkExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.UnpackExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.VariableInitExp;
import org.eclipse.m2m.qvt.oml.ecore.ImperativeOCL.WhileExp;
import org.eclipse.ocl.ecore.AnyType;
import org.eclipse.ocl.ecore.BagType;
import org.eclipse.ocl.ecore.CallExp;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.CollectionType;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.FeatureCallExp;
import org.eclipse.ocl.ecore.InvalidType;
import org.eclipse.ocl.ecore.LiteralExp;
import org.eclipse.ocl.ecore.LoopExp;
import org.eclipse.ocl.ecore.NavigationCallExp;
import org.eclipse.ocl.ecore.NumericLiteralExp;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.ecore.OppositePropertyCallExp;
import org.eclipse.ocl.ecore.OrderedSetType;
import org.eclipse.ocl.ecore.PrimitiveLiteralExp;
import org.eclipse.ocl.ecore.PrimitiveType;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.ecore.SequenceType;
import org.eclipse.ocl.ecore.SetType;
import org.eclipse.ocl.ecore.TemplateParameterType;
import org.eclipse.ocl.ecore.TupleType;
import org.eclipse.ocl.ecore.TypeType;
import org.eclipse.ocl.ecore.VoidType;
import org.eclipse.ocl.expressions.AssociationClassCallExp;
import org.eclipse.ocl.expressions.BooleanLiteralExp;
import org.eclipse.ocl.expressions.CollectionItem;
import org.eclipse.ocl.expressions.CollectionKind;
import org.eclipse.ocl.expressions.CollectionLiteralExp;
import org.eclipse.ocl.expressions.CollectionLiteralPart;
import org.eclipse.ocl.expressions.CollectionRange;
import org.eclipse.ocl.expressions.EnumLiteralExp;
import org.eclipse.ocl.expressions.IfExp;
import org.eclipse.ocl.expressions.IntegerLiteralExp;
import org.eclipse.ocl.expressions.InvalidLiteralExp;
import org.eclipse.ocl.expressions.IterateExp;
import org.eclipse.ocl.expressions.IteratorExp;
import org.eclipse.ocl.expressions.LetExp;
import org.eclipse.ocl.expressions.MessageExp;
import org.eclipse.ocl.expressions.NullLiteralExp;
import org.eclipse.ocl.expressions.OperationCallExp;
import org.eclipse.ocl.expressions.PropertyCallExp;
import org.eclipse.ocl.expressions.RealLiteralExp;
import org.eclipse.ocl.expressions.StateExp;
import org.eclipse.ocl.expressions.StringLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralExp;
import org.eclipse.ocl.expressions.TupleLiteralPart;
import org.eclipse.ocl.expressions.TypeExp;
import org.eclipse.ocl.expressions.UnlimitedNaturalLiteralExp;
import org.eclipse.ocl.expressions.UnspecifiedValueExp;
import org.eclipse.ocl.expressions.Variable;
import org.eclipse.ocl.expressions.VariableExp;
import org.eclipse.ocl.utilities.ExpressionInOCL;

public class UnparsingQVTOVisitor implements QVTOperationalVisitor<Object>
{
	public Object visitModule(Module module)
	{
		this.unparseModule(module,true);
		return null;
	}

	public void unparseModule(Module module, boolean isFirst)
	{
		if ( module instanceof OperationalTransformation )
		{
			this.unparseOperationalTransformation((OperationalTransformation)module,isFirst);
		}
		else if ( module instanceof Library )
		{
			this.unparseLibrary((Library)module,isFirst);
		}
		else
		{
			this.addText("/*___unknownModuleType___*/");
		}
	}

	private void unparseOperationalTransformation(OperationalTransformation operationalTransformation, boolean isFirst)
	{
		if ( isFirst )
		{
			this.unparseModuleImportsForCompilationUnit(operationalTransformation);
			this.unparseModuleModelTypes(operationalTransformation);
		}
		this.unparseTags(operationalTransformation);
		this.unparseOperationalTransformationSignature(operationalTransformation);
		this.unparseModuleImportsForSignature(operationalTransformation);
		this.addText("{");
		this.addLineBreak();
		this.unparseIntermediateClasses(operationalTransformation);
		this.unparseModuleProperties(operationalTransformation);
		this.unparseIntermediateProperties(operationalTransformation);
		this.unparseModuleOperations(operationalTransformation);
		this.addText("}");
		this.addLineBreak();
	}

	public Object visitLibrary(Library library)
	{
		this.unparseLibrary(library,true);
		return null;
	}

	private void unparseLibrary(Library library, boolean isFirst)
	{
		if ( isFirst )
		{
			this.unparseModuleImportsForCompilationUnit(library);
			this.unparseModuleModelTypes(library);
		}
		this.unparseTags(library);
		this.unparseLibrarySignature(library);
		this.unparseModuleImportsForSignature(library);
		this.addText("{");
		this.addLineBreak();
		this.unparseModuleProperties(library);
		this.unparseModuleOperations(library);
		this.addText("}");
		this.addLineBreak();
	}

	private void unparseModuleModelTypes(Module module)
	{
		EList<ModelType> usedModelTypes = module.getUsedModelType();
		
		for (ModelType modelType : usedModelTypes)
		{
			this.unparseModelType(modelType);
		}

		if ( usedModelTypes.size() > 0 ) 
			this.addLineBreak();
	}
	
	public Object visitModelType(ModelType modelType)
	{
		this.unparseModelType(modelType);
		return null;
	}

	private void unparseModelType(ModelType modelType)
	{
		String instanceClassName = modelType.getInstanceClassName();
		if ( "org.eclipse.m2m.internal.qvt.oml.evaluator.ModelInstance".equals(instanceClassName) )
		{
			String name = modelType.getName();
			String conformance = modelType.getConformanceKind();
			String conformanceText = conformance != null ? " \"" + conformance +"\"" : "";
			String nsURI = modelType.getMetamodel().get(0).getNsURI();
			this.addText("modeltype " + name + conformanceText + " uses \"" + nsURI + "\";");
			this.addLineBreak();
			this.modelTypeMap.put(modelType.getMetamodel().get(0),name);
		}
	}

	private void unparseModuleImportsForCompilationUnit(Module module)
	{
		EList<ModuleImport> moduleImportList = module.getModuleImport();
		
		for (ModuleImport moduleImport : moduleImportList)
		{
			this.unparseModuleImportForCompilationUnit(moduleImport);			
		}		

		if ( moduleImportList.size() > 0 ) 
			this.addLineBreak();
	}

	public Object visitModuleImport(ModuleImport moduleImport)
	{
		this.unparseModuleImportForCompilationUnit(moduleImport);		
		return null;
	}

	private void unparseModuleImportForCompilationUnit(ModuleImport moduleImport)
	{
		Module importedModule = moduleImport.getImportedModule();
		
		if ( importedModule instanceof Library )
		{
			this.unparseLibraryImport((Library)importedModule);

		}
		else
		{
//			this.addText("import "+ importedModule.getName() + ";");this.addLineBreak();
		}
		
	}
	
	private void unparseModuleImportsForSignature(Module module)
	{
		EList<ModuleImport> moduleImportList = module.getModuleImport();
		
		for (ModuleImport moduleImport : moduleImportList)
		{
			this.unparseModuleImportForSignature(moduleImport);
			this.addLineBreak();
		}		
	}

	private void unparseModuleImportForSignature(ModuleImport moduleImport)
	{
		ImportKind importKind = moduleImport.getKind();		
		String kindToken = "imports";
		if ( ImportKind.ACCESS.equals(importKind) ) kindToken = "access";
		if ( ImportKind.EXTENSION.equals(importKind) ) kindToken = "extends";

		Module importedModule = moduleImport.getImportedModule();
		
		String libraryText = importedModule instanceof Library ? " library ": " ";
		
		String moduleName = importedModule.getName();
		moduleName = moduleName.replaceAll("\\.", "::");
		this.addText(kindToken + libraryText + moduleName);
		
		EList<ModelType> bindings = moduleImport.getBinding();
		if ( bindings != null && bindings.size() > 0 )
		{
			this.addText("(");
			for (Iterator<ModelType> modelTypeIterator = bindings.iterator(); modelTypeIterator.hasNext();)
			{
				this.addText(modelTypeIterator.next().getName());
				if ( modelTypeIterator.hasNext() )
				{
					this.addText(", ");
				}
			}
			this.addText(")");
		}
	}

	private Set<String> importedLibraries = new HashSet<String>();
	
	private void unparseLibraryImport(Library library)
	{
		String libraryName = library.getName();

		Resource resource = library.eResource();
		String resourceURI = resource.getURI().toString();
		
		if (resourceURI.startsWith("qvto://blackbox/"))
		{
			String unitName = resourceURI.replace("qvto://blackbox/", "");
			libraryName = unitName;
		}
		
		if ( ! this.importedLibraries.contains(libraryName) )
		{
			this.addText("import library " + libraryName + ";");
			this.addLineBreak();
			this.importedLibraries.add(libraryName);
		}

	}
	
	private void unparseTags(Module module)
	{
		EList<EAnnotation> ownedTags = module.getOwnedTag();
		for ( EAnnotation eAnnotation : ownedTags )
		{
			this.unparseTag(eAnnotation);
		}
	}
	
	private void unparseTag(EAnnotation annotation)
	{
		// <tag> ::= 'tag' <tagid> <scoped_identifier> ('=' <tagvalue>)? ';'
		// <tagid> ::= <STRING>
		// <tagvalue> :: <expression>
		String tagidText = (String)	annotation.getDetails().keySet().toArray()[0];
		if ( tagidText.equals(QvtOperationalEnv.TAG_ALIAS) )
		{
			this.unparseAliasTag(annotation);
		}
	}

	private void unparseAliasTag(EAnnotation annotation)
	{
		String tagidText = (String)	annotation.getDetails().keySet().toArray()[0];
		String newNameText = (String) annotation.getDetails().values().toArray()[0];
		EObject referencedObject = annotation.getReferences().get(0);
		if ( referencedObject instanceof EStructuralFeature )
		{
			EStructuralFeature referencedFeature = (EStructuralFeature) referencedObject;
			EClass referencedContainingClass = referencedFeature.getEContainingClass();
			String originalNameText = this.escapeKeywordName(referencedFeature.getName());
			String fqnText = this.constructFullyQualifiedType(referencedContainingClass);
			this.addText("tag ");
			this.addText("\"" + tagidText + "\" " + fqnText + "::" + originalNameText + " = '" + newNameText + "';");
			this.addLineBreak();			
		}
	}

	private void unparseType(EClassifier type)
	{
		if ( type != null )
			this.addText(this.constructFullyQualifiedType(type));
	}
	
	private void unparseHelperContext(VarParameter helperContext)
	{
		if ( helperContext != null )
		{
			String contextString = constructFullyQualifiedType(helperContext.getType());
			this.addText(contextString + "::");
		}
	}

	private Map<EPackage,String> modelTypeMap = new HashMap<EPackage,String>();
	
	private String constructFullyQualifiedType(EClassifier type)
	{
		if ( type instanceof AnyType )
			return this.constructFullyQualifiedAnyType((AnyType)type);
		if ( type instanceof EClass )
			return this.constructFullyQualifiedEClass((EClass)type);
		if ( type instanceof EDataType )
			return this.constructFullyQualifiedEDataType((EDataType)type);
		if ( type instanceof InvalidType )
			return this.constructFullyQualifiedInvalidType((InvalidType)type);
		if ( type instanceof TemplateParameterType )
			return this.constructFullyQualifiedTemplateParameterType((TemplateParameterType)type);
		if ( type instanceof TypeType )
			return this.constructFullyQualifiedTypeType((TypeType)type);
		if ( type instanceof VoidType )
			return this.constructFullyQualifiedVoidType((VoidType)type);
		return "/*___UnknownType___*/";
	}

	private String constructFullyQualifiedAnyType(AnyType type)
	{
		return "OclAny";
	}

	private String constructFullyQualifiedEClass(EClass type)
	{
		if ( type instanceof TupleType )
			return this.constructFullyQualifiedTupleType((TupleType)type);
		
		if ( IntermediateClassFactory.isIntermediateClass(type) )
			return this.constructFullyQualifiedIntermediateClass(type);
		
		return this.constructFullyQualifiedTypeAsPackagePrefixAndName(type);
	}

	private String constructFullyQualifiedEDataType(EDataType type)
	{
		if ( type instanceof CollectionType )
			return this.constructFullyQualifiedCollectionType((CollectionType)type);
		if ( type instanceof PrimitiveType )
			return this.constructFullyQualifiedPrimitiveType((PrimitiveType)type);
		if ( type instanceof TupleType )
			return this.constructFullyQualifiedTupleType((TupleType)type);
		
		return this.constructFullyQualifiedTypeAsPackagePrefixAndName(type);
	}

	private String constructFullyQualifiedCollectionType(CollectionType collectionType)
	{
		if ( collectionType instanceof DictionaryType )
			return this.constructFullyQualifiedDictionaryType((DictionaryType)collectionType);

		String collectionName = "Collection";
		if ( collectionType instanceof ListType )
			collectionName = "List";
		else if ( collectionType instanceof SequenceType )
			collectionName = "Sequence";
		else if ( collectionType instanceof OrderedSetType )
			collectionName = "OrderedSet";
		else if ( collectionType instanceof SetType )
			collectionName = "Set";
		else if ( collectionType instanceof BagType )
			collectionName = "Bag";
		
		String elementTypeText = this.constructFullyQualifiedType(collectionType.getElementType());

		return collectionName + "(" + elementTypeText + ")";
	}

	private String constructFullyQualifiedDictionaryType(DictionaryType dictionaryType)
	{
		String keyTypeText = this.constructFullyQualifiedType(dictionaryType.getKeyType());
		String elementTypeText = this.constructFullyQualifiedType(dictionaryType.getElementType());
		return "Dict(" + keyTypeText + "," + elementTypeText + ")";
	}
	
	private String constructFullyQualifiedPrimitiveType(PrimitiveType primitiveType)
	{
		return primitiveType.getName();
	}
	
	private String constructFullyQualifiedTupleType(TupleType tupleType)
	{
		StringBuffer featuresText = new StringBuffer();
		EList<EStructuralFeature> tupleFeatures = tupleType.getEStructuralFeatures();
		for (Iterator<EStructuralFeature> featureIterator = tupleFeatures.iterator(); featureIterator.hasNext();)
		{
			EStructuralFeature tupleFeature = featureIterator.next();
			String nameText = tupleFeature.getName();
			String fullTypeText = this.constructFullyQualifiedType(tupleFeature.getEType());
			featuresText.append(nameText + ":" + fullTypeText);
			if ( featureIterator.hasNext() )
				featuresText.append(",");
		}
		return "Tuple(" + featuresText.toString() + ")";
	}
	
	private String constructFullyQualifiedInvalidType(InvalidType type)
	{
		return "OclInvalid";
	}

	private String constructFullyQualifiedTemplateParameterType(TemplateParameterType type)
	{
		return "/*___TemplateParameterType___*/";
	}

	private String constructFullyQualifiedTypeType(TypeType type)
	{
		return "/*___TypeType___*/";
	}

	private String constructFullyQualifiedVoidType(VoidType type)
	{
		return "OclVoid";
	}
	
	private String constructFullyQualifiedIntermediateClass(EClassifier intermediateClass)
	{
		return intermediateClass.getName();
	}	
	
	private String constructFullyQualifiedTypeAsPackagePrefixAndName(EClassifier type)
	{
		String name = type.getName();
		String packagePrefix = this.constructPackagePrefix(type.getEPackage());

		return packagePrefix + name;
	}

	private String constructPackagePrefix(EPackage pakkage)
	{
		if ( pakkage instanceof Library )
			return this.constructPackagePrefixForLibrary((Library)pakkage);
			
		String packagePrefix = "";
		while (pakkage != null)
		{
			if (pakkage.getESuperPackage() != null)
			{
				packagePrefix = pakkage.getName() + "::" + packagePrefix;
				pakkage = pakkage.getESuperPackage();
			}
			else
			{
				String modelTypeName = this.modelTypeMap.get(pakkage);
				if ( modelTypeName != null )
				{
					packagePrefix = modelTypeName + "::" + packagePrefix;
				}
				pakkage = null;
			}
		}
		return packagePrefix;
	}

	private String constructPackagePrefixForLibrary(Library library)
	{
		String packagePrefix = library.getName() + "::";
		return packagePrefix;
	}

	private String constructContextString(VarParameter context)
	{
		String contextString = context == null ? "" : this.constructFullyQualifiedType(context.getType()) + "::";
		return contextString;
	}

	private void unparseOperationalTransformationSignature(OperationalTransformation transformation)
	{
		if ( transformation.isIsBlackbox() )
		{
			this.addText("blackbox ");
		}
		
		this.addText("transformation " + transformation.getName());
		
		EList<ModelParameter> modelParameterList = new BasicEList<ModelParameter>(transformation.getModelParameter());

		for (int i = 0; i < modelParameterList.size(); )
		{
			ModelParameter modelParameter = modelParameterList.get(i);
			
			EAnnotation annotation = modelParameter.getEAnnotation(QvtOperationalParserUtil.QVT_AUTOGEN_MODELPARAM_EXPRESSION_URI);
			
			if ( annotation != null )
			{
				modelParameterList.remove(modelParameter);
			}
			else
			{
				i++;
			}
		}
		
		this.addText("(");
		for (Iterator<ModelParameter> iterator = modelParameterList.iterator(); iterator.hasNext();)
		{
			ModelParameter modelParameter = iterator.next();
			
			String nameText = modelParameter.getName() + ":";
			if ( modelParameter.getName().startsWith(QvtOperationalEnv.GENERATED_NAME_SPECIAL_PREFIX) )
			{
				nameText = "/*" + nameText + "*/";
			}
			DirectionKind directionKind = modelParameter.getKind();
			String directionText = directionKind != null ? directionKind.getName() : "/*___directionKindWasNull___*/";
			EParameter representedParameter = modelParameter.getRepresentedParameter();
			String modelTypeText;
			if ( representedParameter == null )
			{
				ModelType modelType = (ModelType) modelParameter.getEType();
				modelTypeText = modelType != null ? modelType.getName() : "/*___modelTypeWasNull___*/";
			}
			else
			{
				ModelType modelType = (ModelType) representedParameter.getEType();
				modelTypeText = modelType != null ? modelType.getName() : "/*___modelTypeOfRepresentedParameterWasNull___*/";
			}
			this.addText(directionText + " " + nameText + modelTypeText);
			if ( iterator.hasNext() )
			{
				this.addText(", ");
			}
		}
		this.addText(")");

		this.addLineBreak();
	}
	
	private void unparseIntermediateClasses(OperationalTransformation transformation)
	{
		EList<EClass> intermediateClasses = transformation.getIntermediateClass();
		for (Iterator<EClass> classIterator = intermediateClasses.iterator(); classIterator.hasNext();) {
			EClass eClass = classIterator.next();
			this.unparseIntermediateClassOrException(eClass);
		}
	}
	
	private void unparseIntermediateClassOrException(EClass clazz)
	{
		if ( clazz == null ) return;
		
		if ( !this.checkIntermediateException(clazz) )
		{
			this.unparseIntermediateClass(clazz);
		}
		else
		{
			this.unparseIntermediateException(clazz);	
		}
	}
	
	private boolean checkIntermediateException(EClass clazz)
	{
		if ( clazz == null ) return false;
		
		EList<EClass> superTypes = clazz.getESuperTypes();
		
		if ( superTypes == null || superTypes.size() == 0 ) return false;
		
		for (Iterator<EClass> typesIterator = superTypes.iterator(); typesIterator.hasNext();)
		{
			EClass eClass = typesIterator.next();
			if (!eClass.getName().equals("Exception")) continue;
			if (!eClass.getEPackage().getName().equals("Stdlib")) continue;
			return true;
		}
		return false;
	}

	private void unparseIntermediateClass(EClass clazz)
	{
		if ( clazz == null ) return;
		
		this.addText("intermediate class " + clazz.getName());
		
		this.unparseIntermediateClassSupertypes(clazz.getESuperTypes());

		this.addLineBreak();
		this.addTextAndIndent("{");
		this.addLineBreak();
		
		this.unparseIntermediateClassProperties(clazz.getEAttributes());
		this.unparseIntermediateClassReferences(clazz.getEReferences());
		
		this.unindentAndAddText("}");
		this.addEmptyLine();
	}

	private void unparseIntermediateException(EClass exceptionClass)
	{
		if ( exceptionClass == null ) return;
		
		this.addText("exception " + exceptionClass.getName());
		this.unparseIntermediateClassSupertypesWithoutExceptionClass(exceptionClass.getESuperTypes());
		this.addText(" {}");
		this.addLineBreak();
	}

	private void unparseIntermediateClassSupertypesWithoutExceptionClass(EList<EClass> superTypes)
	{
		EList<EClass> filteredSuperTypes = this.removeExceptionClassFromClassList(superTypes);
		this.unparseIntermediateClassSupertypes(filteredSuperTypes);
	}

	private EList<EClass> removeExceptionClassFromClassList(EList<EClass> superTypes)
	{
		EList<EClass> filteredClassList = new BasicEList<EClass>();
		for (Iterator<EClass> iterator = superTypes.iterator(); iterator.hasNext();)
		{
			EClass superType = iterator.next();
			if ( superType.getName().equals("Exception") && superType.getEPackage().getName().equals("Stdlib"))
			{
				//filter StdLib::Exception
			}
			else
			{
				filteredClassList.add(superType);
			}
		}
		return filteredClassList;
	}

	private void unparseIntermediateClassSupertypes(EList<EClass> superTypes)
	{
		if ( superTypes == null || superTypes.size() == 0 ) return;
		
		this.addText(" extends ");
		
		for (Iterator<EClass> typesIterator = superTypes.iterator(); typesIterator.hasNext();)
		{
			EClass eClass = typesIterator.next();
			this.unparseType(eClass);
			if ( typesIterator.hasNext() )
				this.addText(", ");			
		}
	}

	private void unparseIntermediateClassProperties(EList<EAttribute> properties)
	{
		if ( properties == null ) return;
		
		for (Iterator<EAttribute> propertyIterator = properties.iterator(); propertyIterator.hasNext();)
		{
			EAttribute property = propertyIterator.next();
			this.unparseIntermediateClassProperty(property);		
		}
	}

	private void unparseIntermediateClassProperty(EAttribute property)
	{
		String assignmentOp = " = ";
		
		String idText = property.isID() ? "<<id>> " : "";
		
		EAnnotation staticAnnotation = property.getEAnnotation(QvtOperationalParserUtil.QVT_NAMESPACE_URI + "/static");
		String staticText = staticAnnotation != null ? "static " : "";
		
		String readonlyText = property.isChangeable() ? "" : "readonly ";

		String variableName = property.getName();		
		
		this.addText(idText + staticText + readonlyText + variableName + " : ");
		this.unparseType(property.getEType());
		if ( property.isMany() )
		{
			int lowerBound = property.getLowerBound();
			String lowerBoundText = (lowerBound != 0) ? String.valueOf(lowerBound) + " .. " : "";
			int upperBound = property.getUpperBound();
			String upperBoundText = (upperBound == -1 ) ? "*" : String.valueOf(upperBound);
			this.addText("[" + lowerBoundText + upperBoundText + "]");
		}
		else
		{
			if ( property.getLowerBound() == 1 )
				this.addText("[1]");
		}
		if ( property.isOrdered() )
			this.addText(" ordered");
				
		org.eclipse.ocl.expressions.OCLExpression<EClassifier> initExpression = QvtOperationalParserUtil.getInitExpression(property); 
		if ( initExpression != null )
		{
			this.addText(assignmentOp);
			this.unparseOCLExpression(initExpression);
		}
		this.addText(";");
		this.addLineBreak();		
	}

	private void unparseIntermediateClassReferences(EList<EReference> references)
	{
		if ( references == null ) return;
		
		for (Iterator<EReference> referenceIterator = references.iterator(); referenceIterator.hasNext();)
		{
			EReference reference = referenceIterator.next();
			this.unparseIntermediateClassReference(reference);
		}
	}

	private void unparseIntermediateClassReference(EReference reference)
	{
		String assignmentOp = " = ";
		
		String variableName = reference.getName();

		EAnnotation staticAnnotation = reference.getEAnnotation(QvtOperationalParserUtil.QVT_NAMESPACE_URI + "/static");
		String staticText = staticAnnotation != null ? "static " : "";
		
		String readonlyText = reference.isChangeable() ? "" : "readonly ";

		if ( reference.isContainment() )
		{
			this.addText("composes ");
		}
		else
		{
			this.addText("references ");
		}
		this.addText(staticText + readonlyText + variableName + " : ");
		this.unparseType(reference.getEType());
		if ( reference.isMany() )
			this.addText("[*]");
		if ( reference.isOrdered() )
			this.addText(" ordered");
		
		EReference opposite = reference.getEOpposite();
		if ( opposite != null )
		{
			this.addText(" opposites " + opposite.getName());
		}
		
		org.eclipse.ocl.expressions.OCLExpression<EClassifier> initExpression = QvtOperationalParserUtil.getInitExpression(reference); 
		if ( initExpression != null )
		{
			this.addText(assignmentOp);
			this.unparseOCLExpression(initExpression);
		}
		this.addText(";");
		this.addLineBreak();		
	}

	private void unparseLibrarySignature(Library library)
	{
		this.addText("library " + library.getName() + "(");
		for (Iterator<ETypeParameter> iterator = library.getETypeParameters().iterator(); iterator.hasNext();)
		{
			ETypeParameter typeParameter = iterator.next();
			ModelType modelType = null;//FIXME visitLibrarySignature
			this.addText(modelType.getName());
			if ( iterator.hasNext() )
			{
				this.addText(", ");
			}
		}
		this.addText(")");
		this.addLineBreak();
	}
	
	private void unparseModuleProperties(Module module)
	{
		EList<EStructuralFeature> configurationProperties = module.getConfigProperty();
		
		EList<EStructuralFeature> properties = module.getEStructuralFeatures();
		
		boolean hadProperty = false;
		
		for ( EStructuralFeature structuralFeature : properties )
		{
			if ( structuralFeature instanceof EAttribute )
			{
				EAttribute attribute = (EAttribute) structuralFeature;
				this.unparseProperty(attribute,configurationProperties.contains(attribute));
				hadProperty = true;
			}
			else if ( structuralFeature instanceof EReference )
			{
				EReference reference = (EReference) structuralFeature;
				this.unparseProperty(reference,configurationProperties.contains(reference));
				hadProperty = true;
			}
		}		

		if ( hadProperty ) 
			this.addLineBreak();
	}
	
	private void unparseProperty(EAttribute property, boolean isConfigurationProperty)
	{
		String propertyName = property.getName();
		this.addText((isConfigurationProperty ? "configuration " : "") +  "property " + propertyName + ": ");
		this.unparseType(property.getEType());
		
		org.eclipse.ocl.expressions.OCLExpression<EClassifier> initExpression = QvtOperationalParserUtil.getInitExpression(property);
		if ( initExpression != null )
		{
			this.addText(" = ");
			this.unparseOCLExpression(initExpression);
		}
		
		this.addText(";");
		this.addLineBreak();
	}

	private void unparseProperty(EReference property, boolean isConfigurationProperty)
	{
		String propertyName = property.getName();
		this.addText((isConfigurationProperty ? "configuration " : "") +  "property " + propertyName + ": ");
		this.unparseType(property.getEType());
		
		org.eclipse.ocl.expressions.OCLExpression<EClassifier> initExpression = QvtOperationalParserUtil.getInitExpression(property);
		if ( initExpression != null )
		{
			this.addText(" = ");
			this.unparseOCLExpression(initExpression);
		}
		
		this.addText(";");
		this.addLineBreak();
	}
	
	private void unparseIntermediateProperties(OperationalTransformation transformation)
	{
		EList<EStructuralFeature> intermediateProperties = transformation.getIntermediateProperty();
		
		for ( EStructuralFeature property : intermediateProperties )
		{
			this.unparseContextualProperty((ContextualProperty)property);
		}
		
		if ( intermediateProperties.size()> 0 )
			this.addLineBreak();
	}
	
	private void unparseModuleOperations(Module module)
	{
		for ( EOperation operation : module.getEOperations() )
		{
			if ( operation instanceof ImperativeOperation )
			{
				this.unparseImperativeOperation((ImperativeOperation)operation);
			}
		}
	}
	
	public Object visitImperativeOperation(ImperativeOperation operation)
	{
		this.unparseImperativeOperation(operation);
		return null;
	}

	private void unparseImperativeOperation(ImperativeOperation operation)
	{
		if ( operation instanceof Constructor )
		{
			this.unparseConstructor((Constructor)operation);
		}
		else if ( operation instanceof EntryOperation )
		{
			this.unparseEntryOperation((EntryOperation)operation);
		}
		else if ( operation instanceof MappingOperation )
		{
			this.unparseMappingOperation((MappingOperation)operation);
		}
		else if ( operation instanceof Helper )
		{
			this.unparseHelper((Helper)operation);
		}
	}
	
	public Object visitConstructor(Constructor constructor)
	{
		this.unparseConstructor(constructor);
		return null;
	}

	private void unparseConstructor(Constructor constructor)
	{
		boolean isBlackbox = constructor.isIsBlackbox();
		if ( isBlackbox )
		{
			this.addText("blackbox ");
		}
		this.addText("constructor ");
		
		this.unparseHelperContext(constructor.getContext());

		this.addText(constructor.getName() + "(");
		this.unparseMappingParameters(constructor.getEParameters());
		this.addText(")");

		this.unparseVarParameters(constructor.getResult(),false);
		
		if ( isBlackbox )
		{
			this.addText(";");
			this.addLineBreak();
		}
		else
		{
			this.unparseConstructorBody((ConstructorBody)constructor.getBody());
		}
		this.addEmptyLine();
	}

	public Object visitConstructorBody(ConstructorBody constructorBody)
	{
		this.unparseConstructorBody(constructorBody);
		return null;
	}

	private void unparseConstructorBody(ConstructorBody constructorBody)
	{
		if ( constructorBody == null )
			this.addText("/*___OperationBodyWasNull___*/");
		else
		{
			this.indent();
			this.unparseOCLExpressionsLinebreakSeparatedInBraces(constructorBody.getContent(),true);
			this.unindent();
		}
	}
	
	public Object visitEntryOperation(EntryOperation entryOperation)
	{
		this.unparseEntryOperation(entryOperation);
		return null;
	}

	private void unparseEntryOperation(EntryOperation entryOperation)
	{
		if ( entryOperation.getResult() != null && entryOperation.getResult().size() > 0 )
		{
			this.addText("query ");
		}
		if ( entryOperation.getContext() != null )
		{
			this.unparseVarParameter(entryOperation.getContext());
		}
		this.addText(entryOperation.getName() + "(");
		this.unparseMappingParameters(entryOperation.getEParameters());
		this.addText(")");
		this.unparseVarParameters(entryOperation.getResult(),true);
		this.addLineBreak();
		this.unparseOperationBody(entryOperation.getBody());
		this.addLineBreak();
	}

	public Object visitOperationBody(OperationBody body)
	{
		this.unparseOperationBody(body);
		return null;
	}

	private void unparseOperationBody(OperationBody body)
	{
		if ( body != null )
		{
			String sectionName = "";
			this.unparseBodySection(sectionName, body.getContent());
		}
		else
		{
			this.addText("/*___OperationBodyWasNull___*/");
		}
	}

	public Object visitMappingOperation(MappingOperation operation)
	{
		this.unparseMappingOperation(operation);
		return null;
	}
	
	private void unparseMappingOperation(MappingOperation operation)
	{
		boolean isBlackbox = operation.isIsBlackbox();
		if ( isBlackbox )
		{
			this.addText("blackbox ");
		}
		
		boolean isAbstract = QvtOperationalParserUtil.isAbstractOperation(operation);
		if ( isAbstract )
		{
			this.addText("abstract ");
		}
		// p.75:
		// mapping inout <contexttype>::<mappingname> (<parameters>,) : <result-parameters>  ...
		
		VarParameter context = operation.getContext();
		String directionKindText = ( context != null && DirectionKind.INOUT.equals(context.getKind())) ? "inout " : ""; 
		
		this.addText("mapping " + directionKindText + this.getFullOperationName(operation) + "(");
		this.unparseMappingParameters(operation.getEParameters());
		this.addText(")");

		this.unparseMappingResultParameters(operation.getResult());
		this.addText(" ");

		// ...
		// <mapping_extension> ::= <mapping_extension_key> <scoped_identifier_list>
		// <mapping_extension_key> ::= 'inherits' | 'merges' | 'disjuncts'
		this.unparseMappingExtension("inherits",operation.getInherited());
		this.unparseMappingExtension("merges",operation.getMerged());
		this.unparseMappingExtension("disjuncts",operation.getDisjunct());

		// <mapping_refinement> ::= 'refines' <scoped_identifier>
		if ( !isBlackbox )
		{
			this.unparseMappingRefinement(operation.getOverridden());
		}
		
		// ...
		// when {<exprs>} where { <exprs>}
		this.unparseMappingConditions("when",operation.getWhen());		
		this.unparseMappingCondition("where",this.guaranteeBooleanExp(operation.getWhere()));
		
		if ( isBlackbox )
		{
			this.addText(";");
			this.addLineBreak();
		}
		else
		{
			boolean needsInit = this.checkMappingNeedsInit(operation);
			this.unparseMappingBody((MappingBody)operation.getBody(),needsInit);
			this.addEmptyLine();
		}
	}
	
	private String getFullOperationName(ImperativeOperation operation)
	{
		String contextString = this.constructContextString(operation.getContext());
		String fullName = contextString + operation.getName();
		return fullName;
	}

	private void unparseMappingResultParameters(EList<VarParameter> resultParameters)
	{
		if ( resultParameters == null || resultParameters.size() == 0 ) return;
		
		this.addText(" : ");
		
		boolean	withName = resultParameters.size() != 1;
		
		for (Iterator<VarParameter> iterator = resultParameters.iterator(); iterator.hasNext();)
		{
			VarParameter parameter = iterator.next();
			this.unparseMappingParameter(parameter,false,withName);
			if ( iterator.hasNext() )
			{
				this.addText(", ");
			}
		}
	}

	private void unparseMappingParameters(EList<EParameter> eParameters)
	{
		for (Iterator<EParameter> iterator = eParameters.iterator(); iterator.hasNext();)
		{
			EParameter parameter = iterator.next();
			this.unparseMappingParameter((VarParameter)parameter,true);
			if ( iterator.hasNext() )
			{
				this.addText(", ");
			}
		}
	}

	private void unparseMappingParameter(VarParameter parameter, boolean withDirectionAndName)
	{
		this.unparseMappingParameter(parameter,withDirectionAndName,withDirectionAndName);
	}
	
	private void unparseMappingParameter(VarParameter parameter, boolean withDirection, boolean withName)
	{
		String directionText = withDirection ? parameter.getKind().getLiteral() : "";
		String separatorText = withDirection && withName ? " " : "";
		String nameText = withName ? parameter.getName() : "";
		String semicolonText = withDirection || withName ? " : " : "";
		this.addText(directionText + separatorText + nameText + semicolonText);
		this.unparseType(parameter.getType());
		if ( parameter instanceof MappingParameter )
		{
			this.unparseExtentReference(((MappingParameter)parameter).getExtent());
		}
	}

	private void unparseMappingExtension(String relates, EList<MappingOperation> extendedOperations)
	{
		if ( extendedOperations != null && extendedOperations.size() > 0)
		{
			this.addLineBreak();
			this.addText(relates + " ");
			for (Iterator<MappingOperation> operationIterator = extendedOperations.iterator(); operationIterator.hasNext();)
			{
				this.addText(this.getFullOperationName(operationIterator.next()));
				if ( operationIterator.hasNext() )
				{
					this.addText(",");
				}
			}
		}
	}
	
	private void unparseMappingRefinement(ImperativeOperation refinedOperation)
	{
		if ( refinedOperation != null )
		{
			this.addLineBreak();
			this.addText("refines ");
			this.addText(this.getFullOperationName(refinedOperation));
			this.addLineBreak();
		}
	}

	private OCLExpression guaranteeBooleanExp(OCLExpression expression)
	{
		if (expression instanceof BlockExp)
		{
			BlockExp block = (BlockExp) expression;
			EList<OCLExpression> body = block.getBody();
			return body.get(0);
		}
		else
		{
			return expression;
		}
	}
	
	private void unparseMappingCondition(String preOrPost, OCLExpression oclExpression)
	{
		if ( oclExpression != null )
		{
			EList<OCLExpression> conditionList = new BasicEList<OCLExpression>();
			conditionList.add(oclExpression);
			this.unparseMappingConditions(preOrPost,conditionList);
		}		
	}

	private void unparseMappingConditions(String preOrPost, EList<OCLExpression> condition)
	{
		if ( condition != null && condition.size() > 0 )
		{
			this.addLineBreak();
			this.addText(preOrPost + " ");
			this.unparseOCLExpressions("{ ",condition,"; "," }",false,false,false);
		}
	}
	
	private boolean checkMappingNeedsInit(MappingOperation operation)
	{
		EList<EParameter> mappingParameters = operation.getEParameters();
		EList<VarParameter> mappingResults = operation.getResult();
		EList<VarParameter> outParameters = new BasicEList<VarParameter>();
		
		for (Iterator<EParameter> iterator = mappingParameters.iterator(); iterator.hasNext();)
		{
			VarParameter mappingParameter = (VarParameter) iterator.next();
			DirectionKind directionKind = mappingParameter.getKind();
			if ( directionKind.equals(DirectionKind.INOUT) || directionKind.equals(DirectionKind.OUT) )
				outParameters.add(mappingParameter);
		}
		
		outParameters.addAll(mappingResults);
		
		for (Iterator<VarParameter> iterator = outParameters.iterator(); iterator.hasNext();)
		{
			VarParameter varParameter = iterator.next();
			EClassifier parameterType = varParameter.getType();
			if ( parameterType instanceof EClass &&  ((EClass)parameterType).isAbstract() ) return true;
		}
		
		return false;
	}

	public Object visitMappingBody(MappingBody mappingBody)
	{
		this.unparseMappingBody(mappingBody,false);
		return null;
	}

	private void unparseMappingBody(MappingBody mappingBody, boolean needsInit)
	{
		boolean hasInit = mappingBody.getInitSection() != null && mappingBody.getInitSection().size() > 0;
		boolean hasPopulation = mappingBody.getContent() != null && mappingBody.getContent().size() > 0;
		boolean hasEnd = mappingBody.getEndSection() != null && mappingBody.getEndSection().size() > 0;
		boolean hasBody = hasInit || hasPopulation || hasEnd;
		if ( hasBody )
		{
			this.addLineBreak();
			this.addTextAndIndent("{");
			this.addLineBreak();
			if ( hasInit || needsInit ) this.unparseBodySection("init",mappingBody.getInitSection());
//			if ( hasPopulation ) this.unparseBodySection("population",mappingBody.getContent());
			if ( hasPopulation ) this.unparsePopulationSection(mappingBody.getContent());
			if ( hasEnd ) this.unparseBodySection("end",mappingBody.getEndSection());
			this.unindentAndAddText("}");
		}
		else
		{
			this.addText(";");
		}
	}
	
	public Object visitHelper(Helper helper)
	{
		this.unparseHelper(helper);
		return null;
	}

	private void unparseHelper(Helper helper)
	{
		boolean isBlackbox = helper.isIsBlackbox();
		
		if ( isBlackbox )
		{
			this.addText("blackbox ");
		}
		
		String keyword = helper.isIsQuery() ? "query " : "helper ";
		this.addText(keyword);

		this.unparseHelperContext(helper.getContext());
		this.addText(helper.getName() + "(");
		this.unparseMappingParameters(helper.getEParameters());
		this.addText(")");

		this.unparseVarParameters(helper.getResult(),true);
		
		if ( isBlackbox )
		{
			this.addText(";");
		}
		else
		{
			this.addLineBreak();
			this.unparseOperationBody(helper.getBody());
		}
		this.addLineBreak();
	}
	
	private void unparsePopulationSection(EList<OCLExpression> content)
	{
		if ( content == null || content.size() == 0 ) return;
		
		if ( content.size() == 1 && content.get(0) instanceof ObjectExp )
		{
			ObjectExp objExp = (ObjectExp) content.get(0);
			org.eclipse.ocl.ecore.Variable referredObject = objExp.getReferredObject();
			if ( referredObject != null )
			{
				if ( referredObject.getName() == "result" )
				{
					ConstructorBody constructorBody = objExp.getBody();
					if ( constructorBody == null )
						return;
					else
					{
						this.unparseOCLExpressionsLinebreakSeparated(constructorBody.getContent(),true);
						return;
					}
				};
			}
		}
		// default handling
		this.unparseBodySection("population", content);
	}
	
	private void unparseBodySection(String sectionName,EList<OCLExpression> content)
	{
		if ( sectionName != null && sectionName.length() > 0 )
		{
			this.addText(sectionName);
			this.addLineBreak();
		}
		this.unparseOCLExpressionsLinebreakSeparatedInBraces(content,true);
		this.addLineBreak();
	}
	
	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>>
		void unparseOCLExpressions(String head, EList<T> content, String separatorSuffix, String tail, boolean suffixAlways, boolean indented, boolean onSeperateLines)
	{
		if ( head != null )
		{
			this.addText(head);
			if ( onSeperateLines )
				this.addLineBreak();
		}
		if ( indented )
			this.indent();
		for (Iterator<T> expressionIterator = content.iterator(); expressionIterator.hasNext();)
		{
			org.eclipse.ocl.expressions.OCLExpression<EClassifier> expression = expressionIterator.next();
			this.unparseOCLExpression(expression);
			if (expressionIterator.hasNext() || suffixAlways)
				this.addText(separatorSuffix);
			if ( onSeperateLines )
				this.addLineBreak();
		}
		if ( indented )
			this.unindent();
		if ( tail != null )
		{
			this.addText(tail);	
			if ( onSeperateLines )
				this.addLineBreak();
		}
	}

	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>> void unparseOCLExpressionsCommaSeparatedInBrackets(EList<T> list)
	{
		this.unparseOCLExpressions("[", list, ",", "]", false, false, false);
	}
	
	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>> void unparseOCLExpressionsCommaSeparatedInParentheses(EList<T> list)
	{
		this.unparseOCLExpressions("(", list, ",", ")", false, false, false);
	}
	
	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>> void unparseOCLExpressionsSemicolonSeparatedInBraces(EList<T> list, boolean suffixAlways)
	{
		this.unparseOCLExpressions("{", list, ";", " }", suffixAlways, false, false);
	}
	
	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>> void unparseOCLExpressionsLinebreakSeparated(EList<T> list, boolean suffixAlways)
	{
		this.unparseOCLExpressions(null, list, ";", null, suffixAlways, false, true);		
	}
	
	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>> void unparseOCLExpressionsLinebreakSeparatedInBraces(EList<T> list, boolean suffixAlways)
	{
		this.unparseOCLExpressions("{", list, ";", "}", suffixAlways, true, true);		
	}
	
	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>> void unparseOCLExpression(T expression)
	{
		if ( expression == null ) return;
		
		if (expression instanceof CallExp)
		{
			this.unparseCallExpressions((CallExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.IfExp)
		{
			this.unparseIfExp((org.eclipse.ocl.ecore.IfExp)expression);
		}
		else if (expression instanceof ImperativeExpression)
		{
			this.unparseImperativeExpressions((ImperativeExpression)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.LetExp)
		{
			this.unparseLetExp((org.eclipse.ocl.ecore.LetExp)expression);
		}
		else if (expression instanceof LiteralExp)
		{
			this.unparseLiteralExpressions((org.eclipse.ocl.ecore.LiteralExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.MessageExp)
		{
			this.unparseMessageExp((org.eclipse.ocl.ecore.MessageExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.StateExp)
		{
			this.unparseStateExp((org.eclipse.ocl.ecore.StateExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.TypeExp)
		{
			this.unparseTypeExp((org.eclipse.ocl.ecore.TypeExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.UnspecifiedValueExp)
		{
			this.unparseUnspecifiedValueExp((org.eclipse.ocl.ecore.UnspecifiedValueExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.VariableExp)
		{
			this.unparseVariableExp((org.eclipse.ocl.ecore.VariableExp)expression);
		}
		else
		{
			//FIXME complete visitOCLExpression
		}
	}

	private void unparseCallExpressions(CallExp expression)
	{
		if (expression instanceof FeatureCallExp)
		{
			this.unparseFeatureCallExpressions((FeatureCallExp)expression);
		}
		else if (expression instanceof LoopExp)
		{
			this.unparseLoopExpressions((LoopExp)expression);
		}
		else if (expression instanceof ResolveExp)
		{
			this.unparseResolveExpressions((ResolveExp)expression);
		}
	}

	private void unparseFeatureCallExpressions(FeatureCallExp expression)
	{
		if (expression instanceof NavigationCallExp)
		{
			this.unparseNavigationCallExpressions((NavigationCallExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.OperationCallExp)
		{
			this.unparseOperationCallExpressions((org.eclipse.ocl.ecore.OperationCallExp)expression);
		}
	}

	private void unparseNavigationCallExpressions(NavigationCallExp expression)
	{
		if (expression instanceof org.eclipse.ocl.ecore.AssociationClassCallExp)
		{
			this.unparseAssociationClassCallExp((org.eclipse.ocl.ecore.AssociationClassCallExp)expression);
		}
		else if (expression instanceof OppositePropertyCallExp)
		{
			this.unparseOppositePropertyCallExp((OppositePropertyCallExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.PropertyCallExp)
		{
			this.unparsePropertyCallExp((org.eclipse.ocl.ecore.PropertyCallExp)expression);
		}
	}

	public Object visitAssociationClassCallExp(AssociationClassCallExp<EClassifier, EStructuralFeature> call)
	{
		this.unparseAssociationClassCallExp(call);
		return null;		
	}
	
	private void unparseAssociationClassCallExp(AssociationClassCallExp<EClassifier, EStructuralFeature> call)
	{
		//call.getReferredAssociationClass();
		this.unparseOCLExpression(call.getSource());
		this.addText("." + call.getNavigationSource().getName());
		this.unparseOCLExpressionsCommaSeparatedInBrackets(call.getQualifier());
		if ( call.isMarkedPre() )
		{
			this.addText("@pre");
		}
	}

	private void unparseOppositePropertyCallExp(OppositePropertyCallExp call)
	{
		//call.getNavigationSource();
		this.unparseOCLExpression(call.getSource());
		this.addText("." + call.getReferredOppositeProperty().getName());
		this.unparseOCLExpressionsCommaSeparatedInBrackets(call.getQualifier());
		if ( call.isMarkedPre() )
		{
			this.addText("@pre");
		}
	}

	public Object visitPropertyCallExp(PropertyCallExp<EClassifier, EStructuralFeature> call)
	{
		this.unparsePropertyCallExp(call);
		return null;
	}

	private void unparsePropertyCallExp(PropertyCallExp<EClassifier, EStructuralFeature> call)
	{
		EStructuralFeature navigationSource = call.getNavigationSource();
		
		org.eclipse.ocl.expressions.OCLExpression<EClassifier> source = call.getSource();
		if (
				( source instanceof VariableExp<?,?> ) &&
				( this.variableNameIsGeneratedTemp(((VariableExp<?,?>)source).getReferredVariable().getName()))
			)
		{
			this.addText("/* " + ((VariableExp<?,?>)source).getReferredVariable().getName() + "*/");
		}
		else
		{
			if ( source != null )
			{
				if ( source instanceof VariableExp<?,?> )
				{
					boolean namePrinted = this.unparseVariableExp((VariableExp<EClassifier,EParameter>)source);
					if ( namePrinted )
					{
						this.addText(".");				
					}					
				}
				else
				{
					this.unparseOCLExpression(source);
					this.addText(".");				
				}
			}
			else
			{
				this.addText("/*___sourceIsNull___*/");				
			}
		}

		
		EStructuralFeature referredProperty = call.getReferredProperty();
		String propertyName = referredProperty.getName();
		String propertyText = escapeKeywordName(propertyName);
		this.addText(propertyText);

		EList<org.eclipse.ocl.expressions.OCLExpression<EClassifier>> qualifier = call.getQualifier();
		if ( qualifier != null && qualifier.size() > 0 )
			this.unparseOCLExpressionsCommaSeparatedInBrackets(qualifier);

		if ( call.isMarkedPre() )
		{
			this.addText("@pre");
		}
	}

	private void unparseOperationCallExpressions(org.eclipse.ocl.ecore.OperationCallExp expression)
	{
		if (expression instanceof ImperativeCallExp)
		{
			this.unparseImperativeCallExpressions((ImperativeCallExp)expression);
		}
		else if (expression instanceof LogExp)
		{
			this.unparseLogExp((LogExp)expression);
		}
		else
		{
			this.unparseOperationCallExp(expression);
		}
	}

	private void unparseImperativeCallExpressions(ImperativeCallExp expression)
	{
		if (expression instanceof MappingCallExp)
		{
			this.unparseMappingCallExp((MappingCallExp)expression);
		}
		else
		{
			this.unparseImperativeCallExp(expression);
		}
	}

	public Object visitMappingCallExp(MappingCallExp expression)
	{
		this.unparseMappingCallExp(expression);
		return null;
	}

	private void unparseMappingCallExp(MappingCallExp expression)
	{
		this.unparseOperationCallExp(expression);
	}

	private void unparseImperativeCallExp(ImperativeCallExp expression)
	{
		this.unparseOperationCallExp(expression);
	}

	public Object visitLogExp(LogExp log)
	{
		this.unparseLogExp(log);
		return null;
	}
	
	private void unparseLogExp(LogExp log)
	{
		this.addText("log");
		this.unparseOCLExpressionsCommaSeparatedInParentheses(log.getArgument());
		OCLExpression condition = log.getCondition();
		if ( condition != null)
		{
			this.addText(" when ");
			this.unparseOCLExpression(condition);
		}
	}
	
	public Object visitOperationCallExp(OperationCallExp<EClassifier, EOperation> expression)
	{
		this.unparseOperationCallExp(expression);
		return null;
	}

	private void unparseOperationCallExp(OperationCallExp<EClassifier, EOperation> expression)
	{
		org.eclipse.ocl.expressions.OCLExpression<EClassifier> source = expression.getSource();
		
		String selector = "";
		boolean parenthesize = false;
		if ( source != null )
		{
			boolean sourceIsCollection = source.getType() instanceof CollectionType;
			selector = sourceIsCollection ? "->" : ".";
			
			if ( source.getType() instanceof Library )
				selector = "";
		}
		
		EOperation referredOperation = expression.getReferredOperation();
		String operationName = referredOperation != null ? referredOperation.getName() : "/*___referredOperationWasNull___*/";

		if ( expression instanceof MappingCallExp )
		{
			String mappingCallText = ((MappingCallExp)expression).isIsStrict() ? "xmap " : "map ";
			operationName = mappingCallText + operationName;
		}

		if ( operationName.equals("=") ||
				operationName.equals("+") ||
				operationName.equals("-") ||
				operationName.equals("*") ||
				operationName.equals("/") ||
				operationName.equals("%") ||
				operationName.equals(">") ||
				operationName.equals("<")
				)
		{
			selector = "";
			parenthesize = true;
		}
		else if ( operationName.equals("<>") )
		{
			selector = "";
			operationName = " != ";
			parenthesize = true;
		}
		else if ( operationName.equals("and") )
		{
			selector = "";
			operationName = " and ";
			parenthesize = true;
		}		
		else if ( operationName.equals("or") )
		{
			selector = "";
			operationName = " or ";
			parenthesize = true;
		}
		
		if ( parenthesize ) this.addText("(");
		
		EList<org.eclipse.ocl.expressions.OCLExpression<EClassifier>> argumentList = expression.getArgument();
		boolean hasArguments = argumentList != null & argumentList.size() > 0;

		if ( operationName.equals("-") && !hasArguments )
		{
			//unary minus
			this.addText(selector + operationName);
			this.unparseOCLExpression((OCLExpression)source);
		}
		else
		{
			if ( source instanceof VariableExp )
			{
				boolean isPrinted = this.unparseVariableExp((VariableExp<EClassifier, EParameter>)source);
				selector = isPrinted ? selector : "";
			}
			else
			{
				this.unparseOCLExpression((OCLExpression)source);
			}
			this.addText(selector + operationName);
			this.unparseArgumentList(argumentList);
		}
		if ( parenthesize ) this.addText(")");
	}

	private <T extends org.eclipse.ocl.expressions.OCLExpression<EClassifier>> void unparseArgumentList(EList<T> argumentList)
	{
		this.addText("(");
		for (Iterator<T> iterator = argumentList.iterator(); iterator.hasNext();)
		{
			org.eclipse.ocl.expressions.OCLExpression<EClassifier> argument = iterator.next();
			this.unparseOCLExpression((OCLExpression)argument);
			if ( iterator.hasNext() )
			{
				this.addText(", ");
			}
		}
		this.addText(")");
	}

	private void unparseLoopExpressions(LoopExp expression)
	{
		if (expression instanceof ImperativeLoopExp)
		{
			this.unparseImperativeLoopExpressions((ImperativeLoopExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.IterateExp)
		{
			this.unparseIterateExp((org.eclipse.ocl.ecore.IterateExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.IteratorExp)
		{
			this.unparseIteratorExp((org.eclipse.ocl.ecore.IteratorExp)expression);
		}
	}

	private void unparseImperativeLoopExpressions(ImperativeLoopExp expression)
	{
		if (expression instanceof ForExp)
		{
			this.unparseForExp((ForExp)expression);
		}
		else if (expression instanceof ImperativeIterateExp)
		{
			this.unparseImperativeIterateExp((ImperativeIterateExp)expression);
		}
	}

	public Object visitForExp(ForExp expression)
	{
		this.unparseForExp(expression);
		return null;
	}
	
	private void unparseForExp(ForExp expression)
	{
		// p 105
		// <source>-><for-name> (<iterator-list> | <condition>) <body> ;
		// <source>->< for-name> (<iterator-list> ) <body> ;
		this.unparseOCLExpression((OCLExpression)expression.getSource());

		this.addText("->" + expression.getName() + "(");
		
		this.unparseLoopExpIterator(expression.getIterator(),false);

		if ( expression.getCondition() != null )
		{
			this.addText("|");			
			this.unparseImperativeLoopExpCondition(expression.getCondition());			
		}
		this.addText(")");
		this.unparseBlockExpBody((OCLExpression)expression.getBody());
	}

	public Object visitImperativeIterateExp(ImperativeIterateExp expression)
	{
		this.unparseImperativeIterateExp(expression);
		return null;
	}

	private void unparseImperativeIterateExp(ImperativeIterateExp expression)
	{
		this.unparseOCLExpression((OCLExpression)expression.getSource());
		
		this.addText("->" + expression.getName() + "(");

		this.unparseLoopExpIterator(expression.getIterator(),true);
		
		if ( expression.getTarget() != null )
		{
			// <source> -> <collector-name> (<iterator-list>; <target> = <body> | <condition>) ;
			this.addText(";");
			this.unparseVariable(expression.getTarget(),false);
			this.addText("=");
			this.unparseOCLExpression((OCLExpression)expression.getBody());
			this.addText("|");
			this.unparseImperativeLoopExpCondition(expression.getCondition());
		}
		else
		{
			// <source> -> <collector-name> (<iterator-list> | <body_or_condition>) ;
			// <source> -> <collector-name> (<body_or_condition>) ;
			this.addText("|");
			this.unparseImperativeLoopExpBodyXorCondition(expression);			
		}
		this.addText(")");
	}

	private void unparseLoopExpIterator(EList<Variable<EClassifier, EParameter>> loopIteratorList, boolean withType)
	{
		if ( loopIteratorList != null && loopIteratorList.size() > 0 )
		{
			for (Iterator<Variable<EClassifier, EParameter>> iterator = loopIteratorList.iterator(); iterator.hasNext();)
			{
				Variable<EClassifier, EParameter> loopIterator =
						(Variable<EClassifier, EParameter>) iterator.next();
				this.unparseVariableNameType(loopIterator,withType);
				if ( iterator.hasNext() )
					this.addText(",");
			}
		}
	}

	private void unparseImperativeLoopExpBodyXorCondition(ImperativeLoopExp expression)
	{
		if ( expression.getBody() != null && expression.getCondition() == null )
		{
			this.unparseLoopExpBody((OCLExpression)expression.getBody());
		}
		else if ( expression.getBody() == null && expression.getCondition() != null )
		{
			this.unparseImperativeLoopExpCondition(expression.getCondition());			
		}
		else
		{
			this.addText("/*body:"+expression.getBody() + "; condition:" + expression.getCondition() + "*/");
		}
	}

	private void unparseLoopExpBody(OCLExpression body)
	{
		this.unparseOCLExpression(body);
	}
	
	private void unparseImperativeLoopExpCondition(OCLExpression condition)
	{
		if ( condition instanceof org.eclipse.ocl.ecore.TypeExp )
		{
			this.unparseTypeExp((org.eclipse.ocl.ecore.TypeExp) condition);
		}
		else
		{
			this.unparseOCLExpression(condition);
		}
	}

	public Object visitIterateExp(IterateExp<EClassifier, EParameter> iterateExp)
	{
		this.unparseIterateExp(iterateExp);
		return null;
	}

	private void unparseIterateExp(IterateExp<EClassifier, EParameter> iterateExp)
	{
//		<iterate_exp> ::= 'iterate' '(' <declarator_list> ';' <declarator> '|' <expression> ')'
		
		this.unparseOCLExpression((OCLExpression)iterateExp.getSource());
		this.addText("->" + "iterate" + "(");
		this.unparseVariablesCommaSeparated(iterateExp.getIterator());
		this.addText(";");
		this.unparseVariable(iterateExp.getResult(),true);
		this.addText("|");
		this.unparseOCLExpression(iterateExp.getBody());
		this.addText(")");
	}

	public Object visitIteratorExp(IteratorExp<EClassifier, EParameter> expression)
	{
		this.unparseIteratorExp(expression);
		return null;
	}

	private void unparseIteratorExp(IteratorExp<EClassifier, EParameter> expression)
	{
		this.unparseOCLExpression((OCLExpression)expression.getSource());
		this.addText("->" + expression.getName() + "(");
		this.unparseLoopExpIterator(expression.getIterator(),true);
		this.addText("|");
		this.unparseLoopExpBody((OCLExpression)expression.getBody());
		this.addText(")");
	}

	private void unparseResolveExpressions(ResolveExp expression)
	{
		if (expression instanceof ResolveInExp)
		{
			this.unparseResolveInExp((ResolveInExp)expression);
		}
		else
		{
			this.unparseResolveExp((ResolveExp)expression);
		}		
	}

	public Object visitResolveExp(ResolveExp expression)
	{
		this.unparseResolveExp(expression);
		return null;
	}

	public Object visitResolveInExp(ResolveInExp expression)
	{
		this.unparseResolveInExp(expression);
		return null;
	}

	private void unparseResolveExp(ResolveExp expression)
	{
		// <resolve_exp> ::= <resolve_key> '(' <resolve_condition>? ')'
		// <resolve_key> ::= 'late'? <resolve_kind>
		// <resolve_kind> ::= 'resolve' | 'resolveone' | 'invresolve' | 'invresolveone'
		// <resolve_condition> ::= <declarator> ('|' <expression>)?
		this.unparseSource(expression.getSource());
				
		String resolveKindText = this.constructResolveKindText(expression);
		this.addText(resolveKindText +"(");
		
		org.eclipse.ocl.ecore.Variable target = expression.getTarget();
		if ( target != null )
		{
			boolean hasCondition = expression.getCondition() != null;
			String targetNameText = hasCondition ? target.getName() + " : " : "";
			this.addText(targetNameText);
			this.unparseType(target.getType());
		}

		this.unparseResolveExpCondition(expression.getCondition());

		this.addText(")");
	}

	private void unparseResolveInExp(ResolveInExp expression)
	{
		// <resolve_in_exp> ::= <resolve_in_key> '(' <scoped_identifier> (',' <resolve_condition>)?')'
		// <resolve_in_key> ::= 'late'? <resolve_in_kind>
		// <resolve_in_kind> ::= 'resolveIn' | 'resolveoneIn' | 'invresolveIn' | 'invresolveoneIn'
		// <resolve_condition> ::= <declarator> ('|' <expression>)?
		this.unparseSource(expression.getSource());
		String resolveInKindText = this.constructResolveKindText(expression);
		resolveInKindText += "In";
		this.addText(resolveInKindText +"(");

		MappingOperation inMapping = expression.getInMapping();
		String inMappingText = inMapping != null ? this.getFullOperationName(inMapping) : "/*___InMappingWasNull___*/";
		this.addText(inMappingText);

		org.eclipse.ocl.ecore.Variable target = expression.getTarget();
		if ( target != null )
		{
			boolean hasCondition = expression.getCondition() != null;
			String targetNameText = hasCondition ? target.getName() + " : " : "";
			this.addText(", " + targetNameText);
			this.unparseType(target.getType());
		}

		this.unparseResolveExpCondition(expression.getCondition());

		this.addText(")");
	}

	private void unparseSource(org.eclipse.ocl.expressions.OCLExpression<EClassifier> source)
	{
		if ( source != null )
		{
			if ( source instanceof VariableExp )
			{
				boolean isPrinted = this.unparseVariableExp((VariableExp<EClassifier, EParameter>)source);
				this.addText(isPrinted ? "." : "");
			}
			else
			{
				this.unparseOCLExpression((OCLExpression)source);
				this.addText(".");
			}
		}
	}

	private String constructResolveKindText(ResolveExp expression)
	{
		String resolveInKindText = "resolve";
		resolveInKindText = expression.isIsInverse() ? "inv" + resolveInKindText : resolveInKindText;
		resolveInKindText += expression.isOne() ? "one" : "";
		resolveInKindText = expression.isIsDeferred() ? ("late " + resolveInKindText) : resolveInKindText;
		return resolveInKindText;
	}

	private void unparseResolveExpCondition(OCLExpression resolveCondition)
	{
		if ( resolveCondition != null )
		{
			this.addText(" | ");
			this.unparseImperativeLoopExpCondition(resolveCondition);
		}
	}

	public Object visitIfExp(IfExp<EClassifier> expression)
	{
		this.unparseIfExp(expression);
		return null;
	}
	
	private void unparseIfExp(IfExp<EClassifier> expression)
	{
		// <if_exp> ::= 'if' <expression> <then_part>
		//				<elif_part>* <else_part>? 'endif'
		// <then_part> ::= 'then' <if_body>
		// <elif_part> ::= 'elif' <if_body>
		// <else_part> ::= 'else' <if_body>
		// <if_body> ::= <expression> | <expression_block>
		this.addText("if (");
		this.unparseOCLExpression((OCLExpression)expression.getCondition());
		this.addText(")");
		this.indent();
		this.addLineBreak();
		this.addTextAndIndent("then ");
		this.unparseOCLExpression((OCLExpression)expression.getThenExpression());
		if ( expression.getElseExpression() != null )
		{
			this.addLineBreak();
			this.unindentAndAddText("else ");
			this.indent();
			this.unparseOCLExpression((OCLExpression)expression.getElseExpression());
		}
		this.addLineBreak();
		this.unindentAndAddText("endif ");
		this.unindent();
	}

	private void unparseImperativeExpressions(ImperativeExpression expression)
	{
		if (expression instanceof AltExp)
		{
			this.unparseAltExp((AltExp) expression);
		}
		else if (expression instanceof AssertExp)
		{
			this.unparseAssertExp((AssertExp) expression);
		}
		else if (expression instanceof AssignExp)
		{
			this.unparseAssignExp((AssignExp) expression);
		}
		else if (expression instanceof BlockExp)
		{
			this.unparseBlockExp((BlockExp) expression, true);
		}
		else if (expression instanceof BreakExp)
		{
			this.unparseBreakExp((BreakExp) expression);
		}
		else if (expression instanceof CatchExp)
		{
			this.unparseCatchExp((CatchExp) expression);
		}
		else if (expression instanceof ComputeExp)
		{
			this.unparseComputeExp((ComputeExp) expression);
		}
		else if (expression instanceof ContinueExp)
		{
			this.unparseContinueExp((ContinueExp) expression);
		}
//		else if (expression instanceof ImperativeCallExp)
//		{
//			this.unparseImperativeCallExpressions((ImperativeCallExp)expression);
//		}
//		else if (expression instanceof ImperativeLoopExp)
//		{
//			this.unparseImperativeLoopExpressions((ImperativeLoopExp)expression);
//		}
		else if (expression instanceof InstantiationExp)
		{
			this.unparseInstantiationExpressions((InstantiationExp) expression);
		}
//		else if (expression instanceof LogExp)
//		{
//			this.unparseLogExp((LogExp) expression);
//		}
		else if (expression instanceof RaiseExp)
		{
			this.unparseRaiseExp((RaiseExp) expression);
		}
		else if (expression instanceof ReturnExp)
		{
			this.unparseReturnExp((ReturnExp) expression);
		}
		else if (expression instanceof SwitchExp)
		{
			this.unparseSwitchExp((SwitchExp) expression);
		}
		else if (expression instanceof TryExp)
		{
			this.unparseTryExp((TryExp) expression);
		}
		else if (expression instanceof UnlinkExp)
		{
			this.unparseUnlinkExp((UnlinkExp) expression);
		}
		else if (expression instanceof UnpackExp)
		{
			this.unparseUnpackExp((UnpackExp) expression);
		}
		else if (expression instanceof VariableInitExp)
		{
			this.unparseVariableInitExp((VariableInitExp) expression);
		}
		else if (expression instanceof WhileExp)
		{
			this.unparseWhileExp((WhileExp) expression);
		}		
	}

	public Object visitAltExp(AltExp astNode)
	{
		this.unparseAltExp(astNode);
		return null;
	}

	private void unparseAltExp(AltExp astNode)
	{
		this.addText("AltExp");
	}

	public Object visitAssertExp(AssertExp assertExpression)
	{
		this.unparseAssertExp(assertExpression);
		return null;
	}
	
	private void unparseAssertExp(AssertExp assertExpression)
	{
		this.addText("assert ");
		
		SeverityKind severity =	assertExpression.getSeverity();
		if ( severity != null )
			this.addText(severity.getLiteral() + " ");
		
		this.addText("(");
		this.unparseOCLExpression(assertExpression.getAssertion());
		this.addText(")");
		
		LogExp logExpression = assertExpression.getLog();
		if ( logExpression != null )
		{
			this.addText(" with ");
			this.unparseLogExp(logExpression);
		}
	}

	public Object visitAssignExp(AssignExp assignment)
	{
		this.unparseAssignExp(assignment);
		return null;
	}
	
	private void unparseAssignExp(AssignExp assignment)
	{
		this.unparseOCLExpression(assignment.getLeft());
		
		String operatorText = assignment.isIsReset() ? " := " : " += ";
		this.addText(operatorText);

		if ( assignment.getValue().size() == 1 )
		{
			this.unparseOCLExpression(assignment.getValue().get(0));
		}
		else
		{
			this.unparseOCLExpressionsSemicolonSeparatedInBraces(assignment.getValue(), false);
		}
	}

	public Object visitBlockExp(BlockExp block)
	{
		this.unparseBlockExp(block,true);
		return null;
	}

	private void unparseBlockExp(BlockExp block, boolean printDo)
	{
		if ( printDo )
			this.addText("do ");
		this.unparseOCLExpressionsSemicolonSeparatedInBraces(block.getBody(), false);
	}

	private void unparseBlockExp(BlockExp block, boolean printDo, boolean suffixAlways)
	{
		if ( printDo )
			this.addText("do ");
		this.unparseOCLExpressionsSemicolonSeparatedInBraces(block.getBody(),suffixAlways);
	}

	public Object visitBreakExp(BreakExp expression)
	{
		this.unparseBreakExp(expression);
		return null;
	}

	private void unparseBreakExp(BreakExp expression)
	{
		this.addText("break");
	}

	public Object visitCatchtExp(CatchExp expression)
	{
		this.unparseCatchExp(expression);
		return null;
	}
	
	private void unparseCatchExpressions(EList<CatchExp> catchExpressions)
	{
		for (Iterator<CatchExp> iterator = catchExpressions.iterator(); iterator.hasNext();)
		{
			CatchExp catchExpression = iterator.next();
			this.unparseCatchExp(catchExpression);
		}
	}

	private void unparseCatchExp(CatchExp expression)
	{
		this.addText("except (");

		EAnnotation catchVarAnnotation = expression.getEAnnotation(QvtOperationalParserUtil.QVT_NAMESPACE_URI + "/catchVar");
		if ( catchVarAnnotation != null )
		{
			this.unparseCatchVarAnnotation(catchVarAnnotation);
		}

		this.unparseCatchExpExceptions(expression.getException());
		
		this.addText(") ");
		this.unparseTryBody(expression.getBody());
	}

	private void unparseCatchVarAnnotation(EAnnotation catchVarAnnotation)
	{
		EList<EObject> contents = catchVarAnnotation.getContents();
		
		if ( contents != null && contents.size() > 0 )
		{
			EObject exceptionObject = contents.get(0);
			
			if ( exceptionObject instanceof Variable<?,?> )
			{
				Variable<EClassifier,EObject> catchVariable = (Variable<EClassifier,EObject>) exceptionObject;
				this.addText(catchVariable.getName() + ":");
			}
		}
	}

	private void unparseCatchExpExceptions(EList<EClassifier> exceptions)
	{
		if ( exceptions == null ) return;
		
		for (Iterator<EClassifier> iterator = exceptions.iterator(); iterator.hasNext();)
		{
			EClassifier exception = iterator.next();
			
			this.addText(exception.getName());
			if ( iterator.hasNext() )
			{
				this.addText(",");
			}
		}
	}

	public Object visitComputeExp(ComputeExp expression)
	{
		this.unparseComputeExp(expression);
		return null;
	}

	private void unparseComputeExp(ComputeExp expression)
	{
		org.eclipse.ocl.ecore.Variable variable = expression.getReturnedElement();
		OCLExpression body = expression.getBody();

		if ( body instanceof WhileExp )
		{
			WhileExp whileExp = (WhileExp) body;
			this.addText("while (");
			this.unparseVariable(variable,true);
			this.addText(";");
			this.unparseOCLExpression(whileExp.getCondition());
			this.addText(")");	
			this.unparseBlockExpBody(whileExp.getBody());
		}
		else
		{
			this.addText("compute (");
			this.unparseVariable(variable,true);
			this.addText(")");	
			this.unparseBlockExpBody(body);
		}		
	}

	public Object visitContinueExp(ContinueExp expression)
	{
		this.unparseContinueExp(expression);
		return null;
	}

	private void unparseContinueExp(ContinueExp expression)
	{
		this.addText("continue");
	}

	private void unparseInstantiationExpressions(InstantiationExp expression)
	{
		if (expression instanceof ObjectExp)
		{
			this.unparseObjectExp((ObjectExp)expression);
		}
		else
		{
			this.unparseInstantiationExp(expression);
		}
	}

	public Object visitObjectExp(ObjectExp expression)
	{
		this.unparseObjectExp(expression);
		return null;
	}
	
	private void unparseObjectExp(ObjectExp expression)
	{
		this.addText("object ");
		if ( expression.getReferredObject() != null )
		{
			this.unparseVariableNameType(expression.getReferredObject());
		}
		this.unparseExtentReference(expression.getExtent());	
		this.addText(" ");
		this.unparseConstructorBody(expression.getBody());
	}

	private void unparseExtentReference(org.eclipse.ocl.ecore.Variable extent)
	{
		if ( extent != null )
		{
			if ( ! extent.getName().startsWith(QvtOperationalEnv.GENERATED_NAME_SPECIAL_PREFIX) )
			{
				this.addText("@" + extent.getName());
			}
		}
	}

	public Object visitInstantiationExp(InstantiationExp expression)
	{
		this.unparseInstantiationExp(expression);
		return null;
	}
	
	private void unparseInstantiationExp(InstantiationExp expression)
	{
		this.addText("new ");
		this.unparseType(expression.getType());
		this.unparseExtentReference(expression.getExtent());
		this.unparseArgumentList(expression.getArgument());
	}

	public Object visitRaiseExp(RaiseExp raise)
	{
		this.unparseRaiseExp(raise);
		return null;
	}
	
	private void unparseRaiseExp(RaiseExp raise)
	{
		// <raise_exp> ::= 'raise' <scoped_identifier> ('(' <arg_list>? ')')?
		// <arg_list> ::= <expression_comma_list>
		raise.getArgument();
		raise.getException();
		this.addText("raise ");
		this.unparseType(raise.getException());
		if ( raise.getArgument() != null )
		{
			this.addText("(");
			this.unparseOCLExpression(raise.getArgument());
			this.addText(")");
		}
	}

	public Object visitReturnExp(ReturnExp expression)
	{
		this.unparseReturnExp(expression);
		return null;
	}

	private void unparseReturnExp(ReturnExp expression)
	{
		//<return_exp> ::= 'return' <expression>?
		
		this.addText("return");
		if ( expression.getValue() != null )
		{
			this.addText(" ");
			this.unparseOCLExpression(expression.getValue());
		}
	}

	public Object visitSwitchExp(SwitchExp svvitch)
	{
		this.unparseSwitchExp(svvitch);
		return null;
	}

	private void unparseSwitchExp(SwitchExp svvitch)
	{
		// <switch_exp> ::= 'switch' ('(' <iter_declarator> ')')? <switch_body>
		// <switch_body> ::= '{' <switch_alt>+ <switch_else>? '}'
		// <switch_alt> ::= 'case' '(' <expression> ')' <expression_statement>
		// <switch_else> ::= 'else' <expression_statement>
		svvitch.getElsePart();

		this.addTextAndIndent("switch {");
		this.addLineBreak();
		EList<AltExp> altExpList = svvitch.getAlternativePart();
		for (AltExp altExp : altExpList)
		{
			this.addText("case (");
			this.unparseOCLExpression(altExp.getCondition());
			this.addText(") ");
			this.unparseOCLExpression(altExp.getBody());
			this.addText(";");
			this.addLineBreak();
		}
		if ( svvitch.getElsePart() != null )
		{
			this.addText("else ");
			this.unparseOCLExpression(svvitch.getElsePart());
			this.addText(";");
			this.addLineBreak();
		}
		this.unindentAndAddText("}");
	}

	public Object visitTryExp(TryExp expression)
	{
		this.unparseTryExp(expression);
		return null;
	}

	private void unparseTryExp(TryExp expression)
	{
		this.addText("try ");
		EList<OCLExpression> tryBody = expression.getTryBody();
		this.unparseTryBody(tryBody);
		this.unparseCatchExpressions(expression.getExceptClause());
	}

	private void unparseTryBody(EList<OCLExpression> tryBody)
	{
		EList<OCLExpression> unparseBody = tryBody;
		
		if ( unparseBody.size() == 1 &&  unparseBody.get(0) instanceof BlockExp )
		{
			BlockExp block = (BlockExp) unparseBody.get(0);
			unparseBody = block.getBody();
		}
		
		this.unparseOCLExpressionsLinebreakSeparatedInBraces(unparseBody,true);
		this.addLineBreak();
	}

	public Object visitUnlinkExp(UnlinkExp unlink)
	{
		this.unparseUnlinkExp(unlink);
		return null;
	}

	private void unparseUnlinkExp(UnlinkExp unlink)
	{
		this.unparseOCLExpression(unlink.getTarget());
		this.addText(".unlink(");
		this.unparseOCLExpression(unlink.getItem());
		this.addText(")");
	}

	public Object visitUnpackExp(UnpackExp unpack)
	{
		this.unparseUnpackExp(unpack);
		return null;
	}

	private void unparseUnpackExp(UnpackExp unpack)
	{
		this.addText("var ");
		this.unparseOCLVariablesCommaSeparated(unpack.getTargetVariable());
		this.addText(" := ");
		this.unparseOCLExpression(unpack.getSource());
	}

	private void unparseOCLVariablesCommaSeparated(EList<org.eclipse.ocl.ecore.Variable> list)
	{
		this.unparseOCLVariables("","",list,",","");
	}

	private void unparseOCLVariables(String head, String prefix, EList<org.eclipse.ocl.ecore.Variable> list, String separatorSuffix, String tail)
	{
		this.addText(head);
		for (Iterator<org.eclipse.ocl.ecore.Variable> iterator = list.iterator(); iterator.hasNext();)
		{
			org.eclipse.ocl.ecore.Variable variable = iterator.next();
			this.addText(prefix);
			this.unparseVariable(variable,false);
			if (iterator.hasNext())
				this.addText(separatorSuffix);
		}
		this.addText(tail);
	}

	private void unparseVariablesCommaSeparated(EList<Variable<EClassifier, EParameter>> list)
	{
		this.unparseVariables("","",list,",","");
	}

	private void unparseVariables(String head, String prefix, EList<Variable<EClassifier, EParameter>> list, String separatorSuffix, String tail)
	{
		this.addText(head);
		for (Iterator<Variable<EClassifier, EParameter>> iterator = list.iterator(); iterator.hasNext();)
		{
			Variable<EClassifier, EParameter> variable = iterator.next();
			this.addText(prefix);
			this.unparseVariable(variable,false);
			if (iterator.hasNext())
				this.addText(separatorSuffix);
		}
		this.addText(tail);
	}


	public Object visitVariableInitExp(VariableInitExp variableInit)
	{
		this.unparseVariableInitExp(variableInit);
		return null;
	}

	private void unparseVariableInitExp(VariableInitExp variableInit)
	{
		org.eclipse.ocl.ecore.Variable variable = variableInit.getReferredVariable();
		String assignmentOp = variableInit.isWithResult() ? "::=" : ":=";
		
		this.addText("var ");
		this.unparseVariable(variable,false);
		OCLExpression initExpression = (OCLExpression)variable.getInitExpression();
		if ( initExpression != null )
		{
			this.addText(assignmentOp);
			this.unparseOCLExpression(initExpression);
		}
	}

	public Object visitWhileExp(WhileExp vvhile)
	{
		this.unparseWhileExp(vvhile);
		return null;
	}

	private void unparseWhileExp(WhileExp vvhile)
	{
		this.addText("while ( ");
		this.unparseOCLExpression(vvhile.getCondition());
		this.addText(" )");		
		
		this.unparseBlockExpBody(vvhile.getBody());
	}

	private void unparseBlockExpBody(OCLExpression body)
	{
		if ( body instanceof BlockExp )
		{
			BlockExp block = (BlockExp) body;
			this.unparseBlockExp(block,false);
		}
		else
		{
			this.addText("{ ");
			this.unparseOCLExpression(body);
			this.addText(" }");
		}
	}

	public Object visitLetExp(LetExp<EClassifier, EParameter> letExp)
	{
		this.unparseLetExp(letExp);
		return null;
	}

	private void unparseLetExp(LetExp<EClassifier, EParameter> letExp)
	{
		// <let_exp> ::= 'let' <declarator_list> 'in' <expression>
		this.addText("let ");
		this.unparseVariable(letExp.getVariable(),true);
		this.addText(" in ");
		this.unparseOCLExpression(letExp.getIn());
	}

	private void unparseLiteralExpressions(LiteralExp expression)
	{
		if (expression instanceof org.eclipse.ocl.ecore.CollectionLiteralExp)
		{
			this.unparseCollectionLiteralExp((org.eclipse.ocl.ecore.CollectionLiteralExp) expression);
		}
		else if (expression instanceof DictLiteralExp)
		{
			this.unparseDictLiteralExp((DictLiteralExp) expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.EnumLiteralExp)
		{
			this.unparseEnumLiteralExp((org.eclipse.ocl.ecore.EnumLiteralExp) expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.InvalidLiteralExp)
		{
			this.unparseInvalidLiteralExp((org.eclipse.ocl.ecore.InvalidLiteralExp) expression);
		}
		else if (expression instanceof ListLiteralExp)
		{
			this.unparseListLiteralExp((ListLiteralExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.NullLiteralExp)
		{
			this.unparseNullLiteralExp((org.eclipse.ocl.ecore.NullLiteralExp) expression);
		}
		else if (expression instanceof OrderedTupleLiteralExp)
		{
			this.unparseOrderedTupleLiteralExp((OrderedTupleLiteralExp) expression);
		}
		else if (expression instanceof PrimitiveLiteralExp)
		{
			this.unparsePrimitiveLiteralExpressions((PrimitiveLiteralExp)expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.TupleLiteralExp)
		{
			this.unparseTupleLiteralExp((org.eclipse.ocl.ecore.TupleLiteralExp) expression);
		}
	}
	
	public Object visitCollectionLiteralExp(CollectionLiteralExp<EClassifier> literalExp)
	{
		this.unparseCollectionLiteralExp(literalExp);
		return null;
	}

	private void unparseCollectionLiteralExp(CollectionLiteralExp<EClassifier> literalExp)
	{
		if (literalExp.getKind().equals(CollectionKind.SEQUENCE_LITERAL) && literalExp.getType() instanceof ListType )
		{
			this.unparseCollectionLiteralAsListLiteral(literalExp);
			return;
		}
		String collectionTypeText = literalExp.getKind().getName();
		EList<CollectionLiteralPart<EClassifier>> partList = literalExp.getPart();
		this.unparseCollectionLiteralExp_TypeAndParts(collectionTypeText, partList);
		//literalExp.isSimpleRange();
	}

	private void unparseCollectionLiteralExp_TypeAndParts(String collectionTypeText,EList<CollectionLiteralPart<EClassifier>> partList)
	{
		this.addText(collectionTypeText + " {");
		this.unparseCollectionLiteralParts(partList);
		this.addText("}");
	}
	
	private void unparseCollectionLiteralParts(EList<CollectionLiteralPart<EClassifier>> collectionLiteralPartList)
	{
		for (Iterator<CollectionLiteralPart<EClassifier>> partItor = collectionLiteralPartList.iterator(); partItor.hasNext();)
		{
			this.unparseCollectionLiteralPart(partItor.next());
			if ( partItor.hasNext() ) this.addText(",");
		}
	}

	private void unparseCollectionLiteralPart(CollectionLiteralPart<EClassifier> collectionLiteralPart)
	{
		if ( collectionLiteralPart instanceof CollectionItem )
		{
			this.unparseCollectionItem((CollectionItem<EClassifier>) collectionLiteralPart);
		}
		else if ( collectionLiteralPart instanceof CollectionRange )
		{
			this.unparseCollectionRange((CollectionRange<EClassifier>) collectionLiteralPart);
		}
		else
		{
			this.addText("/*CollectionLiteralPart*/");
		}
	}
	
	public Object visitDictLiteralExp(DictLiteralExp dictLiteral)
	{
		this.unparseDictLiteralExp(dictLiteral);
		return null;
	}

	private void unparseDictLiteralExp(DictLiteralExp dictLiteral)
	{
		//		<literal_dict> ::= 'Dict' '{' <dict_item_list>? '}'
		//		<dict_item_list> ::= <dict_item> (',' <dict_item>)*
		//		<dict_item> ::= <literal_simple> '=' <expression>
		this.addText("Dict {");
		this.unparseDictLiteralParts(dictLiteral.getPart());
		this.addText("}");
	}

	private void unparseDictLiteralParts(EList<DictLiteralPart> dictLiteralPartList)
	{
		for (Iterator<DictLiteralPart> partItor = dictLiteralPartList.iterator(); partItor.hasNext();)
		{
			this.unparseDictLiteralPart(partItor.next());
			if ( partItor.hasNext() ) this.addText(",");
		}
	}

	public Object visitDictLiteralPart(DictLiteralPart dictLiteralPart)
	{
		this.unparseDictLiteralPart(dictLiteralPart);
		return null;
	}

	private void unparseDictLiteralPart(DictLiteralPart dictLiteralPart)
	{
		this.unparseOCLExpression(dictLiteralPart.getKey());
		this.addText(" = ");
		this.unparseOCLExpression(dictLiteralPart.getValue());
	}

	public Object visitEnumLiteralExp(EnumLiteralExp<EClassifier, EEnumLiteral> enumLiteral)
	{
		this.unparseEnumLiteralExp(enumLiteral);
		return null;
	}

	private void unparseEnumLiteralExp(EnumLiteralExp<EClassifier, EEnumLiteral> enumLiteral)
	{
		EEnumLiteral referredEnumLiteral = enumLiteral.getReferredEnumLiteral();
		EClassifier enumType = enumLiteral.getType();
		this.unparseType(enumType);

		String enumName = enumLiteral.getName();			

		if ( referredEnumLiteral != null )
		{
			enumName = referredEnumLiteral.getLiteral();		
		}
		
		enumName = escapeKeywordName(enumName);

		this.addText("::" + enumName);
	}

	public Object visitInvalidLiteralExp(InvalidLiteralExp<EClassifier> invalidLiteral)
	{
		this.unparseInvalidLiteralExp(invalidLiteral);
		return null;
	}

	private void unparseInvalidLiteralExp(InvalidLiteralExp<EClassifier> invalidLiteral)
	{
		this.addText("invalid/*___InvalidLiteralExp___*/");
	}

	private void unparseListLiteralExp(ListLiteralExp listLiteral)
	{
		this.addText("List ");
		this.unparseOCLExpressionsSemicolonSeparatedInBraces(listLiteral.getElement(), false);
	}

	private void unparseCollectionLiteralAsListLiteral(CollectionLiteralExp<EClassifier> collectionLiteral)
	{
		this.unparseCollectionLiteralExp_TypeAndParts("List", collectionLiteral.getPart());
	}

	public Object visitNullLiteralExp(NullLiteralExp<EClassifier> literalExp)
	{
		this.unparseNullLiteralExp(literalExp);
		return null;
	}

	private void unparseNullLiteralExp(NullLiteralExp<EClassifier> literalExp)
	{
		this.addText("null");
	}

	public Object visitOrderedTupleLiteralExp(OrderedTupleLiteralExp tuple)
	{
		this.unparseOrderedTupleLiteralExp(tuple);
		return null;
	}

	private void unparseOrderedTupleLiteralExp(OrderedTupleLiteralExp tuple)
	{
		this.addText("Tuple {");
		this.unparseOrderedTupleLiteralParts(tuple.getPart());
		this.addText("}");
	}

	private void unparseOrderedTupleLiteralParts(EList<OrderedTupleLiteralPart> partList)
	{
		for (Iterator<OrderedTupleLiteralPart> partItor = partList.iterator(); partItor.hasNext();)
		{
			this.unparseOrderedTupleLiteralPart(partItor.next());
			if ( partItor.hasNext() ) this.addText(",");
		}
	}

	public Object visitOrderedTupleLiteralPart(OrderedTupleLiteralPart part)
	{
		this.unparseOrderedTupleLiteralPart(part);
		return null;
	}

	private void unparseOrderedTupleLiteralPart(OrderedTupleLiteralPart part)
	{
		this.unparseOCLExpression(part.getValue());
	}

	private void unparsePrimitiveLiteralExpressions(PrimitiveLiteralExp expression)
	{
		if (expression instanceof org.eclipse.ocl.ecore.BooleanLiteralExp)
		{
			this.unparseBooleanLiteralExp((org.eclipse.ocl.ecore.BooleanLiteralExp) expression);
		}
		else if (expression instanceof NumericLiteralExp)
		{
			this.unparseNumericLiteralExp((NumericLiteralExp) expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.StringLiteralExp)
		{
			this.unparseStringLiteralExp((org.eclipse.ocl.ecore.StringLiteralExp) expression);
		}
	}

	public Object visitBooleanLiteralExp(BooleanLiteralExp<EClassifier> literal)
	{
		this.unparseBooleanLiteralExp(literal);
		return null;
	}

	private void unparseBooleanLiteralExp(BooleanLiteralExp<EClassifier> literal)
	{
		this.addText(literal.getBooleanSymbol().toString());
	}

	private void unparseNumericLiteralExp(NumericLiteralExp expression)
	{
		if (expression instanceof org.eclipse.ocl.ecore.IntegerLiteralExp)
		{
			this.unparseIntegerLiteralExp((org.eclipse.ocl.ecore.IntegerLiteralExp) expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.RealLiteralExp)
		{
			this.unparseRealLiteralExp((org.eclipse.ocl.ecore.RealLiteralExp) expression);
		}
		else if (expression instanceof org.eclipse.ocl.ecore.UnlimitedNaturalLiteralExp)
		{
			this.unparseUnlimitedNaturalLiteralExp((org.eclipse.ocl.ecore.UnlimitedNaturalLiteralExp) expression);
		}
	}
	
	public Object visitIntegerLiteralExp(IntegerLiteralExp<EClassifier> literal)
	{
		this.unparseIntegerLiteralExp(literal);
		return null;
	}

	private void unparseIntegerLiteralExp(IntegerLiteralExp<EClassifier> literal)
	{
		this.addText(literal.getIntegerSymbol().toString());
	}

	public Object visitRealLiteralExp(RealLiteralExp<EClassifier> literal)
	{
		this.unparseRealLiteralExp(literal);
		return null;
	}

	private void unparseRealLiteralExp(RealLiteralExp<EClassifier> literal)
	{
		this.addText(literal.getRealSymbol().toString());
	}

	public Object visitUnlimitedNaturalLiteralExp(UnlimitedNaturalLiteralExp<EClassifier> literal)
	{
		this.unparseUnlimitedNaturalLiteralExp(literal);
		return null;
	}

	private void unparseUnlimitedNaturalLiteralExp(UnlimitedNaturalLiteralExp<EClassifier> literal)
	{
		this.addText(literal.toString());
	}

	public Object visitStringLiteralExp(StringLiteralExp<EClassifier> literal)
	{
		this.unparseStringLiteralExp(literal);
		return null;
	}

	private void unparseStringLiteralExp(StringLiteralExp<EClassifier> literal)
	{
		String stringText = literal.getStringSymbol();
		stringText = stringText.replace("\\", "\\\\");
		stringText = stringText.replace("'", "\\'");
		stringText = stringText.replace("\r", "\\r");
		stringText = stringText.replace("\n", "\\n");
		stringText = stringText.replace("\t", "\\t");
		this.addText("'" + stringText + "'");
	}

	public Object visitTupleLiteralExp(TupleLiteralExp<EClassifier, EStructuralFeature> tuple)
	{
		this.unparseTupleLiteralExp(tuple);
		return null;
	}

	private void unparseTupleLiteralExp(TupleLiteralExp<EClassifier, EStructuralFeature> tuple)
	{
		//		<literal_tuple> ::= 'Tuple' '{' <tuple_item_list>? '}'
		//		<tuple_item_list> ::= <declarator_list>
		this.addText("Tuple {");
		this.unparseTupleLiteralParts(tuple.getPart());
		this.addText("}");
	}

	private void unparseTupleLiteralParts(EList<TupleLiteralPart<EClassifier, EStructuralFeature>> tupleLiteralPartList)
	{
		for (Iterator<TupleLiteralPart<EClassifier, EStructuralFeature>> partItor = tupleLiteralPartList.iterator(); partItor.hasNext();)
		{
			this.unparseTupleLiteralPart(partItor.next());
			if ( partItor.hasNext() ) this.addText(",");
		}
	}

	public Object visitTupleLiteralPart(TupleLiteralPart<EClassifier, EStructuralFeature> part)
	{
		this.unparseTupleLiteralPart(part);
		return null;
	}

	private void unparseTupleLiteralPart(TupleLiteralPart<EClassifier, EStructuralFeature> part)
	{
		this.addText(part.getName() + ":");
		this.unparseType(part.getType());
		this.addText(" = ");
		this.unparseOCLExpression(part.getValue());
	}

	public Object visitMessageExp(MessageExp<EClassifier, CallOperationAction, SendSignalAction> message)
	{
		this.unparseMessageExp(message);
		return null;
	}

	private void unparseMessageExp(MessageExp<EClassifier, CallOperationAction, SendSignalAction> message)
	{
		this.unparseOCLExpression(message.getTarget());
		if ( message.getCalledOperation() != null )
		{
			this.addText("^^" + message.getCalledOperation().getOperation().getName());
		}
		else
		{
			this.addText("^^" + message.getSentSignal().getSignal().getName());
		}
		this.unparseOCLExpressionsCommaSeparatedInParentheses(message.getArgument());
	}

	public Object visitStateExp(StateExp<EClassifier, EObject> state)
	{
		this.unparseStateExp(state);
		return null;
	}

	private void unparseStateExp(StateExp<EClassifier, EObject> state)
	{
		this.addText("oclIsInState(" + state.getReferredState() +")");
	}

	public Object visitTypeExp(TypeExp<EClassifier> expression)
	{
		this.unparseTypeExp(expression);
		return null;
	}

	private void unparseTypeExp(TypeExp<EClassifier> expression)
	{
		this.unparseType(expression.getReferredType());
	}

	public Object visitUnspecifiedValueExp(UnspecifiedValueExp<EClassifier> unspecExp)
	{
		this.unparseUnspecifiedValueExp(unspecExp);
		return null;
	}

	private void unparseUnspecifiedValueExp(UnspecifiedValueExp<EClassifier> unspecExp)
	{
		this.addText("unspecified");
	}

	public Object visitVariableExp(VariableExp<EClassifier, EParameter> variableExp)
	{
		this.unparseVariableExp(variableExp);
		return null;
	}

	private boolean unparseVariableExp(VariableExp<EClassifier, EParameter> variableExp)
	{
		String variableName = variableExp.getName();
		EClassifier variableType = variableExp.getType();
		Variable<EClassifier,EParameter> referredVariable = variableExp.getReferredVariable();
		
		if ( referredVariable == null )
		{
			this.addText("/*noReferredVariable*/");
			this.addText(variableName);
			return true;
		}
		else
		{
			if (variableType instanceof Library )
			{
				String libraryName = variableType.getName();
				this.addText("/*" + libraryName + "." + variableName + "*/");
				return false;
			}
			else if (variableType instanceof OperationalTransformation )
			{
				String transformationName = variableType.getName();
				if ( variableName.equals("this") )
				{
					this.addText("/*" + transformationName + "." + variableName + "*/");
					return false;					
				}
				else
				{
					this.addText(variableName);
					return true;
				}
			}
			else
			{
//				this.addText("/*VariableExp*/" + variableName);
				this.addText(variableName);
				return true;
			}
		}
		
	}

	private void VISIT_OCL_EXPRESSIONS__END()
	{
		
	}

	public Object visitCollectionItem(CollectionItem<EClassifier> collectionItem)
	{
		this.unparseCollectionItem(collectionItem);
		return null;
	}

	private void unparseCollectionItem(CollectionItem<EClassifier> collectionItem)
	{
		this.unparseOCLExpression(collectionItem.getItem());
	}

	public Object visitCollectionRange(CollectionRange<EClassifier> range)
	{
		this.unparseCollectionRange(range);
		return null;
	}

	private void unparseCollectionRange(CollectionRange<EClassifier> range)
	{
		this.unparseOCLExpression(range.getFirst());
		this.addText("..");
		this.unparseOCLExpression(range.getLast());
	}

	public Object visitVariable(Variable<EClassifier, EParameter> variable)
	{
		this.unparseVariable(variable,false);
		return null;
	}

	private void unparseVariable(Variable<EClassifier, EParameter> variable, boolean withInitExpression)
	{
		if ( variable == null ) return;
		
		this.unparseVariableNameType(variable);
		
		if ( withInitExpression )
		{
			org.eclipse.ocl.expressions.OCLExpression<EClassifier> initExpression = variable.getInitExpression();
			if ( initExpression != null )
			{
				this.addText(" = ");
				this.unparseOCLExpression(initExpression);
			}
		}
	}

	private void unparseVariableNameType(Variable<EClassifier, EParameter> variable)
	{
		this.unparseVariableNameType(variable,true);
	}

	private void unparseVariableNameType(Variable<EClassifier, EParameter> variable, boolean withType)
	{
		String variableName = variable.getName();
		boolean variableNameIsNotGenerated = !this.variableNameIsGeneratedTemp(variableName);
		if ( variableNameIsNotGenerated )
		{
			this.addText(variableName);
		}
		else
		{
			this.addText("/*" + variableName + "*/");
		}

		if ( withType )
		{
			if ( variableNameIsNotGenerated )
			{
				this.addText(":");
			}
			this.unparseType(variable.getType());
		}
		
//		TODO: Grok Variable, VariableInit etc		
//		org.eclipse.ocl.expressions.OCLExpression<EClassifier> initExpression = variable.getInitExpression();
//		if ( initExpression != null )
//		{
//			this.addText("=");
//			this.unparseOCLExpression(initExpression);
//		}
	}

	private boolean variableNameIsGeneratedTemp(String variableName)
	{
		boolean isGeneratedTemp =
				variableName.startsWith(QvtEnvironmentBase.GENERATED_NAME_SPECIAL_PREFIX + "temp");
		return isGeneratedTemp;
	}

	public Object visitExpressionInOCL(ExpressionInOCL<EClassifier, EParameter> expression)
	{
		this.unparseExpressionInOCL(expression);
		return null;
	}

	private void unparseExpressionInOCL(ExpressionInOCL<EClassifier, EParameter> expression)
	{
		this.addText("[TODO]"); //TODO visitExpressionInOCL
	}

	public Object visitConstraint(Constraint constraint)
	{
		this.unparseConstraint(constraint);
		return null;
	}

	private void unparseConstraint(Constraint constraint)
	{
		this.addText("[TODO]"); //TODO visitConstraint
		constraint.getConstrainedElements();
		constraint.getSpecification();
		constraint.getStereotype();
	}

	public Object visitContextualProperty(ContextualProperty contextualProperty)
	{
		this.unparseContextualProperty(contextualProperty);
		return null;
	}

	private void unparseContextualProperty(ContextualProperty contextualProperty)
	{
//		contextualProperty.getOverridden();
		String readonlyText = contextualProperty.isChangeable() ? "" : "readonly ";

		EClass contextClass = contextualProperty.getContext();
		String contextClassText = this.constructFullyQualifiedType(contextClass);
		
		String propertyName = contextualProperty.getName();
		this.addText("intermediate " + readonlyText + "property " + contextClassText + "::" + propertyName + ": ");
		this.unparseType(contextualProperty.getEType());
		
		org.eclipse.ocl.expressions.OCLExpression<EClassifier> initExpression = contextualProperty.getInitExpression();
		if ( initExpression != null )
		{
			this.addText(" = ");
			this.unparseOCLExpression(initExpression);
		}
		
		this.addText(";");
		this.addLineBreak();
	}

	public Object visitVarParameter(VarParameter parameter)
	{
		this.unparseVarParameter(parameter);
		return null;
	}

	private void unparseVarParameter(VarParameter parameter)
	{
		if ( parameter instanceof MappingParameter )
		{
			this.unparseMappingParameter((MappingParameter)parameter,true);
		}
		else
		{
			this.unparseVarParameter(parameter,true);
		}
	}
	
	private void unparseVarParameter(VarParameter parameter, boolean withDirectionAndName)
	{
		String direction = parameter.getKind().getLiteral();
		String name = parameter.getName();
		String directionAndName = withDirectionAndName ? "" + direction + " " + name + " : " : "";
		
		this.addText(directionAndName);
		
		this.unparseVarParameterType(parameter.getType());
	}
	
	private void unparseVarParameterType(EClassifier parameterType)
	{
		if (parameterType instanceof CollectionType )
		{
			this.unparseVarParameterCollectionType((CollectionType)parameterType);
		}
		else
		{
			this.unparseType(parameterType);
		}
	}
	
	private void unparseVarParameterCollectionType(CollectionType collectionType)
	{
		if ( collectionType instanceof ListType ) {
			this.addText("List (");
		}
		else if ( collectionType instanceof SequenceType ) {
			this.addText("Sequence (");
		}
		else if ( collectionType instanceof OrderedSetType ) {
			this.addText("OrderedSet (");
		}
		else if ( collectionType instanceof SetType ) {
			this.addText("Set (");
		}
		else if ( collectionType instanceof BagType ) {
			this.addText("Bag (");
		}
		else if ( collectionType instanceof DictionaryType ) {
			this.addText("Dict (");
			DictionaryType dictionaryType = (DictionaryType) collectionType;
			this.unparseVarParameterType(dictionaryType.getKeyType());
			this.addText(",");
		}
		else
		{
			this.addText("Collection (");
		}
		
		this.unparseVarParameterType(collectionType.getElementType());
		
		this.addText(")");
	}

	private void unparseVarParameters(EList<VarParameter> parameters, boolean withDirectionAndName)
	{
		if ( parameters == null || parameters.size() == 0 ) return;
		
		this.addText(" : ");
		
		if ( parameters.size() == 1 )
		{
			withDirectionAndName = false;
		}
		
		for (Iterator<VarParameter> iterator = parameters.iterator(); iterator.hasNext();)
		{
			VarParameter parameter = iterator.next();
			this.unparseVarParameter(parameter,withDirectionAndName);
			if ( iterator.hasNext() )
			{
				this.addText(", ");
			}
		}
	}
	
	private String escapeKeywordName(String name)
	{
		if ( QvtKeywords.KEYWORDS.contains(name) )
		{
			return "_" + name;
		}
		else
		{
			return name;
		}
	}
	
	/**
	 * Handling of generated text fragments
	 */
	
	private List<String> lines = new ArrayList<String>();
	private StringBuilder currentLine = new StringBuilder(1024);
	
	private int indentationLevel = 0;
	private String[] indentation = {
			"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t",
			"\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t",
			"\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t"
			};
	
	private void indent()
	{
		this.indentationLevel++;
	}
	
	private void unindent()
	{
		if ( this.indentationLevel > 0 )
			this.indentationLevel--;
	}
	
	public  List<String> getLines()
	{
		if ( this.currentLine.length() > 0 )
		{
			this.addLineBreak();
		}
		return this.lines;
	}
	
	private void addText(String text)
	{
		if ( text == null || text.isEmpty() ) return;
		
		if ( this.currentLine.length() == 0 )
		{
			this.currentLine.append(this.indentation[this.indentationLevel]);
		}
		this.currentLine.append(text);
	}
	
	private void addTextAndIndent(String text)
	{
		this.addText(text);
		this.indent();
	}
	
	private void unindentAndAddText(String text)
	{
		this.unindent();
		this.addText(text);
	}
	
	private void addLineBreak()
	{
		this.lines.add(this.currentLine.toString());
		this.currentLine.setLength(0);
	}

	private void addEmptyLine()
	{
		if ( this.currentLine.length() > 0 )
		{
			this.addLineBreak();
		}
		this.addLineBreak();
	}


	/*
	 * Notes on QVToParser issues
	 * 
	 * Helper.isQuery not set
	 * Library.isBlackbox not set
	 * CollectionLiteralExp.kind always "Set"
	 */
	
	
}

Back to the top