project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/cpp/epoc/engine/model/mmp/MMPView.java
changeset 0 fb279309251b
child 610 bfb3ab3f70f2
child 1589 f8fc355016ca
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project/com.nokia.carbide.cpp.epoc.engine/src/com/nokia/carbide/internal/cpp/epoc/engine/model/mmp/MMPView.java	Fri Apr 03 23:33:03 2009 +0100
@@ -0,0 +1,1331 @@
+/*
+* Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+
+package com.nokia.carbide.internal.cpp.epoc.engine.model.mmp;
+
+import com.nokia.carbide.cpp.epoc.engine.EpocEnginePlugin;
+import com.nokia.carbide.cpp.epoc.engine.image.IMultiImageSource;
+import com.nokia.carbide.cpp.epoc.engine.model.mmp.*;
+import com.nokia.carbide.cpp.epoc.engine.preprocessor.IDefine;
+import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.*;
+import com.nokia.carbide.internal.api.cpp.epoc.engine.dom.mmp.*;
+import com.nokia.carbide.internal.cpp.epoc.engine.model.*;
+import com.nokia.carbide.internal.cpp.epoc.engine.parser.ITranslationUnitParser;
+import com.nokia.carbide.internal.cpp.epoc.engine.parser.mmp.IMMPParserConfiguration;
+import com.nokia.carbide.internal.cpp.epoc.engine.preprocessor.IConditionalBlock;
+import com.nokia.cpp.internal.api.utils.core.*;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.osgi.framework.Version;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * View onto MMP contents.
+ *
+ */
+public class MMPView extends ViewASTBase implements IMMPView {
+	
+	static final String SOURCE_INFO_PATTERN = "SOURCE|SOURCEPATH"; //$NON-NLS-1$
+
+	static final String BITMAP_KEYWORD = "BITMAP"; //$NON-NLS-1$
+
+	static final String RESOURCE_KEYWORD = "RESOURCE"; //$NON-NLS-1$
+	
+	private static final String DEF_FILE_EXTENSION = "def"; //$NON-NLS-1$
+	private static final Pattern VERSION_PATTERN = Pattern.compile("(.*)\\{.*\\}"); //$NON-NLS-1$
+
+	private List<IPath> sources;
+	private List<IPath> documents;
+	private List<IPath> userIncludes;
+	private List<IPath> systemIncludes;
+	private Map<EMMPStatement, List<String>> stringListArgumentSettings;
+	private List<IMMPBitmap> bitmaps;
+	private Set<EMMPStatement> flagSettings;
+	private Map<EMMPStatement, String> singleArgumentSettings;
+	private List<IMMPResource> resourceBlocks;
+	private List<IMMPAIFInfo> aifs;
+
+	private List<IPath> userResources;
+	private List<IPath> systemResources;
+	private List<EMMPLanguage> languages;
+	private String uid2;
+	private String uid3;
+	private Map<String, String> options, linkerOptions, replaceOptions;
+	
+	IPath defFileBase;
+	
+	private List<IASTStatement> knownStatements;
+
+	protected OrderedObjectMap<IMMPAIFInfo, IASTMMPAifStatement> aifToStatementMap;
+	protected OrderedObjectMap<IMMPResource, IASTMMPStartBlockStatement> resourceBlockToStatementMap;
+	protected OrderedObjectMap<IMMPBitmap, IASTMMPStartBlockStatement> bitmapBlockToStatementMap;
+	
+	/** read-only list for now */
+	private Set<IPath> sourcePaths;
+
+	public MMPView(ModelBase model, ITranslationUnitParser parser,
+			IMMPViewConfiguration viewConfiguration) {
+		super(model, parser, viewConfiguration);
+		
+		//System.out.println("Parsing view for " + model.getPath());
+		
+		stringListArgumentSettings = new LinkedHashMap<EMMPStatement, List<String>>();
+		singleArgumentSettings = new LinkedHashMap<EMMPStatement, String>();
+		flagSettings = new LinkedHashSet<EMMPStatement>();
+		
+		for (EMMPStatement stmt : EMMPStatement.values()) {
+			if (((IMMPViewConfiguration) getViewConfiguration()).isStatementSupported(stmt)) {
+				switch (stmt.getCategory()) {
+				case IMMPParserConfiguration.LIST_ARGUMENT_STATEMENT:
+					if (isStringListArgumentSettingStatement(stmt)) {
+						stringListArgumentSettings.put(stmt, new ArrayList<String>());
+					}
+					break;
+				case IMMPParserConfiguration.SINGLE_ARGUMENT_STATEMENT:
+					if (isSingleArgumentSettingStatement(stmt)) {
+						singleArgumentSettings.put(stmt, null);
+					}
+					break;
+				}
+			} 
+		}
+
+		knownStatements = new ArrayList<IASTStatement>();
+
+		sources = new ArrayList<IPath>();
+		documents = new ArrayList<IPath>();
+		
+		userIncludes = new ArrayList<IPath>();
+		systemIncludes = new ArrayList<IPath>();
+		userResources = new ArrayList<IPath>();
+		systemResources = new ArrayList<IPath>();
+		resourceBlocks = new ArrayList<IMMPResource>();
+		languages = new ArrayList<EMMPLanguage>();
+		
+		bitmaps = new ArrayList<IMMPBitmap>();
+		aifs = new ArrayList<IMMPAIFInfo>();
+		options = new LinkedHashMap<String, String>();
+		linkerOptions = new LinkedHashMap<String, String>();
+		replaceOptions = new LinkedHashMap<String, String>();
+		
+		defFileBase = convertModelToProjectPath(new Path("")); // //$NON-NLS-1$
+		
+		sourcePaths = new LinkedHashSet<IPath>();
+		
+		aifToStatementMap = new OrderedObjectMap<IMMPAIFInfo, IASTMMPAifStatement>();
+		resourceBlockToStatementMap = new OrderedObjectMap<IMMPResource, IASTMMPStartBlockStatement>();
+		bitmapBlockToStatementMap = new OrderedObjectMap<IMMPBitmap, IASTMMPStartBlockStatement>();
+	}
+	
+	IASTMMPTranslationUnit mmp() {
+		return (IASTMMPTranslationUnit) getFilteredTranslationUnit();
+	}
+	
+	/**
+	 * Identify a path which should not be converted to be project-relative.
+	 * This includes actual absolute paths and special EPOCROOT\epoc32
+	 * paths, which look relative but start with "+".
+	 * @param mmpPath
+	 * @return
+	 */
+	static boolean isAbsoluteLikePath(IPath mmpPath) {
+		return (mmpPath.isAbsolute() 
+				|| (mmpPath.segmentCount() > 0 && mmpPath.segment(0).equals("+")));  //$NON-NLS-1$
+	}
+	
+	/**
+	 * Convert an MMP-relative string filename to a project-relative path if possible.
+	 * @param filename 
+	 * @return
+	 */
+	IPath fromMmpToProjectPath(IPath mmpPath) {
+		if (isAbsoluteLikePath(mmpPath))
+			return mmpPath;
+		IPath path = currentDirectory != null ? currentDirectory.append(mmpPath) : mmpPath;
+		if (FileUtils.isPathInParent(path)) {
+			path = getProjectPath().append(path);
+		}
+		return path;
+	}
+
+	/**
+	 * Convert an MMP-relative string filename to a project-relative path if possible.
+	 * @param filename 
+	 * @return
+	 */
+	IPath fromMmpToProjectPath(String mmpPath) {
+		return fromMmpToProjectPath(FileUtils.createPossiblyRelativePath(mmpPath));
+	}
+
+	/**
+	 * Convert an MMP-relative string filename to a project-relative path
+	 * if possible.
+	 * @param mmpPath text node from MMP
+	 * @return
+	 */
+	IPath fromMmpToProjectPath(IASTLiteralTextNode mmpPath) {
+		return fromMmpToProjectPath(mmpPath.getValue());
+	}
+
+	/**
+	 * Convert a project-relative IPath to an MMP-relative path and
+	 * leave other paths alone. 
+	 * @param projectPath 
+	 * @return MMP-relative directory (never blank) or self if not project-relative 
+	 */
+	IPath fromProjectToMmpPath(IPath projectPath) {
+		if (isAbsoluteLikePath(projectPath))
+			return projectPath;
+		IPath path = fromProjectToRelativePath(currentDirectory, projectPath);
+		if (path.segmentCount() == 0)
+			return new Path("."); //$NON-NLS-1$
+		else
+			return path;
+	}
+
+
+	/**
+	 * Create an EMMPLanguage list from the given LANG statement.
+	 * @param prevLang
+	 * @return
+	 */
+	public List<EMMPLanguage> createLanguageList(IASTMMPListArgumentStatement stmt) {
+		List<EMMPLanguage> list = new ArrayList<EMMPLanguage>();
+		for (IASTLiteralTextNode node : stmt.getArguments()) {
+			try {
+				list.add(EMMPLanguage.fromCode(node.getValue()));
+			} catch (IllegalArgumentException e) {
+				// ignore unknown language
+				EpocEnginePlugin.log(
+						Logging.newStatus(EpocEnginePlugin.getDefault(), 
+								new IllegalArgumentException(
+										"Warning, ignoring unknown language code: " + node.getValue() + " in " + stmt)
+				));
+			}
+		}
+		return list;
+	}
+
+	/**
+	 * Create a LANG statement with the given languages.
+	 * @param list
+	 * @return
+	 */
+	public IASTMMPListArgumentStatement createLanguageStatement(Collection<EMMPLanguage> list) {
+		IASTListNode<IASTLiteralTextNode> languages = ASTMMPFactory.createLiteralTextNodeList();
+		for (EMMPLanguage lang : list)
+			languages.add(ASTMMPFactory.createPreprocessorLiteralTextNode(lang.getCodeString()));
+		return ASTMMPFactory.createMMPListArgumentStatement(
+				ASTMMPFactory.createPreprocessorLiteralTextNode(EMMPStatement.LANG.toString()), 
+				languages);
+	}
+
+	/**
+	 * Tell if the statement is a list argument statement that we store
+	 * in lists (rather than in some other manner).
+	 * @return
+	 */
+	public boolean isStringListArgumentSettingStatement(EMMPStatement stmt) {
+		if (stmt.getCategory() != IMMPParserConfiguration.LIST_ARGUMENT_STATEMENT)
+			return false;
+		 return stmt != EMMPStatement.SOURCE
+			&& stmt != EMMPStatement.USERINCLUDE && stmt != EMMPStatement.SYSTEMINCLUDE
+			&& stmt != EMMPStatement.RESOURCE && stmt != EMMPStatement.SYSTEMRESOURCE
+			&& stmt != EMMPStatement.LANG && stmt != EMMPStatement.DOCUMENT;
+	}
+
+	/**
+	 * Tell if the statement is a single argument statement that we store
+	 * in the single-argument settings (rather than in some other manner).
+	 * @return true: yup
+	 */
+	public boolean isSingleArgumentSettingStatement(EMMPStatement stmt) {
+		if (stmt.getCategory() != IMMPParserConfiguration.SINGLE_ARGUMENT_STATEMENT)
+			return false;
+		return stmt != EMMPStatement.SOURCEPATH;	
+	}
+	
+	/**
+	 * Tell if the statement is a flag statement that we store in the
+	 * flag settings
+	 */
+	public boolean isFlagSettingStatement(EMMPStatement stmt) {
+		return stmt.getCategory() == IMMPParserConfiguration.FLAG_STATEMENT;
+	}
+
+	/**
+	 * Get the default project-relative directory for source files.
+	 * @return project-relative path, maybe empty if at project
+	 */
+	IPath defaultProjectRelativeSourcePath() {
+		return currentDirectory;
+	}
+
+	/**
+	 * Return an appropriate path for a SOURCEPATH statement
+	 * @param mmpSourceElement filename
+	 * @return the directory of the filename, or "." if at root
+	 */
+	IPath getSourcePathFromSource(IPath mmpSourceElement) {
+		return mmpSourceElement.segmentCount() > 1 ? mmpSourceElement.removeLastSegments(1)
+				: new Path("."); //$NON-NLS-1$
+	}
+
+	static public boolean equalPath(IPath path, IPath path2) {
+		if (path == null || path2 == null)
+			return false;
+		if (path.segmentCount() == 0 && path2.segmentCount() == 1 && path2.segment(0).equals(".")) { //$NON-NLS-1$
+			return true;
+		}
+		if (path2.segmentCount() == 0 && path.segmentCount() == 1 && path.segment(0).equals(".")) { //$NON-NLS-1$
+			return true;
+		}
+		//return path.toOSString().equalsIgnoreCase(path2.toOSString());
+		// don't ignore capitalization, otherwise there's no way the client can use the API
+		// to repair it
+		return path.equals(path2);
+	}
+
+	/**
+	 * Tell whether a statement can be "seen" at a global level.
+	 * This means anything at the top level and anything inside a START PLATFORM block.
+	 * Stuff from other blocks are local to that block and not globally visible.
+	 * @param stmt
+	 * @return true if statement is global
+	 */
+	boolean isGloballyVisibleStatement(IASTStatement stmt) {
+		if (stmt.getParent() == mmp().getNodes())
+			return true;
+		
+		// only handle nested statements in START PLATFORM.
+		IASTNode parent = stmt;
+		while ((parent = parent.getParent()) != null) {
+			if (parent instanceof IASTMMPStartBlockStatement) {
+				String blockType = ((IASTMMPStartBlockStatement) parent).getBlockType().getValue();
+				if (blockType.equals(RESOURCE_KEYWORD) || blockType.equals(BITMAP_KEYWORD))
+					return false;
+				return isMacroDefined(blockType.toUpperCase());
+			}
+		}
+	
+		// what is this statement?
+		Check.checkState(false);
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.internal.cpp.epoc.engine.model.ViewBase#internalRevertChanges()
+	 */
+	@Override
+	protected void internalRevertChanges() {
+		knownStatements.clear();
+		
+		aifToStatementMap.clear();
+		bitmapBlockToStatementMap.clear();
+		resourceBlockToStatementMap.clear();
+		
+		MMPStatementScanner scanner = new MMPStatementScanner(this);
+		
+		flagSettings.clear();
+		singleArgumentSettings.clear();
+		stringListArgumentSettings.clear();
+		for (EMMPStatement stmt : EMMPStatement.values()) {
+			if (((IMMPViewConfiguration) getViewConfiguration()).isStatementSupported(stmt)) {
+				switch (stmt.getCategory()) {
+				case IMMPParserConfiguration.LIST_ARGUMENT_STATEMENT:
+					if (isStringListArgumentSettingStatement(stmt)) {
+						stringListArgumentSettings.put(stmt, new ArrayList<String>());
+					}
+					break;
+				case IMMPParserConfiguration.SINGLE_ARGUMENT_STATEMENT:
+					if (isSingleArgumentSettingStatement(stmt)) {
+						singleArgumentSettings.put(stmt, null);
+					}
+					break;
+				}
+			} 
+		}		
+		sources.clear();
+		documents.clear();
+		userIncludes.clear();
+		systemIncludes.clear();
+		bitmaps.clear();
+		flagSettings.clear();
+		resourceBlocks.clear();
+		aifs.clear();
+		userResources.clear();
+		systemResources.clear();
+		languages.clear();
+		uid2 = null;
+		uid3 = null;
+		options.clear();
+		linkerOptions.clear();
+		replaceOptions.clear();
+		
+		defFileBase = convertModelToProjectPath(new Path("")); // //$NON-NLS-1$
+		
+		sourcePaths.clear();
+		
+		for (IASTTopLevelNode tln : mmp().getNodes()) {
+			if (!(tln instanceof IASTMMPStatement))
+				continue;
+			IASTMMPStatement statement = (IASTMMPStatement) tln;
+			scanner.scanStatement(statement);
+			
+			// record slash direction
+			recordSlashInfo(statement.getOriginalText());
+		}
+		
+		if (debug) {
+			System.out.println("#internalRevertChanges() for " + getModel().getPath() + ":\n"+ //$NON-NLS-1$ //$NON-NLS-2$
+					"Incoming filtered TU:\n"+ //$NON-NLS-1$
+					getFilteredTranslationUnit().getNewText());
+			dumpModelStructure();
+		}
+	}
+
+
+	private void dumpModelStructure() {
+		dumpCollection("Sources", sources); //$NON-NLS-1$
+		dumpCollection("Documents", documents); //$NON-NLS-1$
+		dumpCollection("User Includes", userIncludes); //$NON-NLS-1$
+		dumpCollection("System Includes", userIncludes); //$NON-NLS-1$
+		dumpCollection("Flags", flagSettings); //$NON-NLS-1$
+		dumpCollection("Single Arguments", singleArgumentSettings); //$NON-NLS-1$
+		dumpCollection("String Lists", stringListArgumentSettings); //$NON-NLS-1$
+		dumpCollection("Bitmaps", bitmaps); //$NON-NLS-1$
+		dumpCollection("Resource Blocks", resourceBlocks); //$NON-NLS-1$
+		dumpCollection("User Resources", userResources); //$NON-NLS-1$
+		dumpCollection("System Resources", systemResources); //$NON-NLS-1$
+		dumpCollection("Languages", languages); //$NON-NLS-1$
+		dumpCollection("AIFs", aifs); //$NON-NLS-1$
+		dumpCollection("Options", options); //$NON-NLS-1$
+		dumpCollection("Linker Options", linkerOptions); //$NON-NLS-1$
+		dumpCollection("Replace Options", replaceOptions); //$NON-NLS-1$
+		System.out.println("DEF file: " + getSingleArgumentSettings().get(EMMPStatement.DEFFILE) + " in " + defFileBase); //$NON-NLS-1$ //$NON-NLS-2$
+		System.out.println("UID: " + uid2 + " " + uid3); //$NON-NLS-1$ //$NON-NLS-2$
+		dumpCollection("SourcePaths", sourcePaths); //$NON-NLS-1$
+	}
+
+	private void dumpCollection(String string, Collection coll) {
+		if (!coll.isEmpty()) {
+			System.out.println(string + ":"); //$NON-NLS-1$
+			for (Object obj : coll) {
+				System.out.println("\t" + obj); //$NON-NLS-1$
+			}
+		}
+	}
+
+	private void dumpCollection(String string, Map map) {
+		if (!map.isEmpty()) {
+			System.out.println(string + ":"); //$NON-NLS-1$
+			for (Map.Entry ent : (Set<Map.Entry>)map.entrySet()) {
+				if (ent.getValue() != null && !(ent.getValue() instanceof Collection && ((Collection)ent.getValue()).isEmpty()))
+					System.out.println("\t" + ent.getKey() + ": " + ent.getValue()); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.internal.cpp.epoc.engine.model.mmp.MMPView#internalGatherChanges(java.util.List, java.util.List)
+	 */
+	@Override
+	protected void internalGatherChanges(List modifications, List messages) {
+		if (debug) {
+			System.out.println("#internalGatherChanges for " + getModel().getPath() + ":\n"); //$NON-NLS-1$ //$NON-NLS-2$
+			dumpModelStructure();
+		}
+		
+		MMPStatementUpdater updater = new MMPStatementUpdater(this, modifications, messages);
+		updater.update();
+		
+		if (debug) {
+			System.out.println(
+					"New filtered TU:\n"+ //$NON-NLS-1$
+					getFilteredTranslationUnit().getNewText());
+					
+		}
+	}
+
+	private boolean statementUsesSourcePath(IASTMMPStatement statement) {
+		if (statement.getKeywordName() == null)
+			return false;
+		
+		if (statement.getKeywordName().matches("(?i)SOURCE|RESOURCE|SYSTEMRESOURCE|DOCUMENT")) {//$NON-NLS-1$
+			return true;
+		}
+		if (statement instanceof IASTMMPStartBlockStatement) {
+			return "RESOURCE".equalsIgnoreCase(((IASTMMPStartBlockStatement) statement).getBlockType().getValue()); //$NON-NLS-1$
+		}
+		return false;
+	}
+
+
+	private void deleteStatement(IASTNode node) {
+		((IASTListHolder) node.getParent()).getList().remove(node);
+	}
+	
+	/**
+	 * Remove stray SOURCEPATH statements
+	 *
+	 */
+	private void cleanupSourcePaths(IASTTranslationUnit tu) {
+		IASTMMPStatement lastSourcePath = null;
+		IConditionalBlock lastSourcePathBlock = null;
+		boolean usedSourcePath = true;
+
+		IASTTopLevelNode[] nodes = (IASTTopLevelNode[]) tu.getNodes().toArray(new IASTTopLevelNode[tu.getNodes().size()]);
+		for (IASTTopLevelNode node : nodes) {
+			if (!(node instanceof IASTMMPStatement))
+				continue;
+			IASTMMPStatement stmt = (IASTMMPStatement) node;
+			IConditionalBlock block = getLangNodeConditionalBlock(stmt);
+			if (EMMPStatement.SOURCEPATH.matches(stmt)) {
+				if (!usedSourcePath
+				&&  (lastSourcePathBlock == block || (lastSourcePathBlock != null && lastSourcePathBlock.contains(block)))) {
+					deleteStatement(lastSourcePath);
+				}
+				lastSourcePath = stmt;
+				lastSourcePathBlock = block;
+				usedSourcePath = false;
+			}
+			else if (statementUsesSourcePath(stmt)) {
+				usedSourcePath = true;
+			}
+		}
+		
+		if (lastSourcePath != null && !usedSourcePath) {
+			deleteStatement(lastSourcePath);
+		}
+	}
+	
+	
+	private void addMissingSourcePaths(IASTTranslationUnit tu) {
+		IASTMMPSingleArgumentStatement lastSourcePathStatement = null;
+		updateCurrentDirectory(tu);
+		IPath lastSourcePath = defaultProjectRelativeSourcePath();
+		
+		// scan top-level nodes (only -- not inside START or the like) to see
+		// if every statement that has a reference to a SOURCEPATH is still
+		// "seeing" the same node (or equivalent)
+		//
+		for (ListIterator<IASTTopLevelNode> iter = tu.getNodes().listIterator();
+			iter.hasNext();) {
+			IASTTopLevelNode tln = iter.next();
+			updateCurrentDirectory(tln);
+			if (!(tln instanceof IASTMMPStatement)) {
+				continue;
+			}
+			IASTMMPStatement stmt = (IASTMMPStatement) tln;
+			if (EMMPStatement.SOURCEPATH.matches(stmt)) {
+				lastSourcePathStatement = (IASTMMPSingleArgumentStatement) tln;
+				lastSourcePath = fromMmpToProjectPath(lastSourcePathStatement.getArgument());
+			} else if (stmt.getSourcePathDependentContext() != null) {
+				// see if this statement can use the last sourcepath
+				IASTMMPSingleArgumentStatement reqdSourcePathStmt =
+					stmt.getSourcePathDependentContext().getSourcePathStatement();
+				IPath reqdSourcePath = lastSourcePath;
+				
+				if (reqdSourcePathStmt != null) {
+					if (reqdSourcePathStmt == IMMPSourcePathDependentContext.DEFAULT_SOURCEPATH_STATEMENT)
+						reqdSourcePath = defaultProjectRelativeSourcePath();
+					else
+						reqdSourcePath = fromMmpToProjectPath(reqdSourcePathStmt.getArgument());
+				}
+				
+				if (!equalPath(lastSourcePath, reqdSourcePath)) {
+					// we're working only inside one TU at the moment, so
+					// it's fine to assume that equal IPaths means equal text,
+					// so we can copy the actual node originally referenced
+					iter.previous();
+					if (reqdSourcePathStmt != null) {
+						lastSourcePathStatement = (IASTMMPSingleArgumentStatement) reqdSourcePathStmt.copy();
+					} else {
+						lastSourcePathStatement = ASTMMPFactory.createMMPSingleArgumentStatement(
+								EMMPStatement.SOURCEPATH.toString(),
+								pathString(fromProjectToMmpPath(reqdSourcePath)));
+					}
+					iter.add(lastSourcePathStatement);
+					iter.next();
+					lastSourcePath = reqdSourcePath;
+				}
+			}
+		}
+		
+	}
+	
+	@Override
+	protected void internalFinalizePreparserTranslationUnit(IASTTranslationUnit tu) {
+		addMissingSourcePaths(tu);
+		cleanupSourcePaths(tu);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.internal.cpp.epoc.engine.model.ViewBase#internalHasChanges()
+	 */
+	@Override
+	protected boolean internalHasChanges() {
+		return true;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.internal.cpp.epoc.engine.model.ViewBase#internalMerge(com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTTranslationUnit)
+	 */
+	@Override
+	protected boolean internalMerge(IASTTranslationUnit oldTu) {
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#createMMPBitmap()
+	 */
+	public IMMPBitmap createMMPBitmap() {
+		return new MMPBitmap();
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#createMMPAIFInfo()
+	 */
+	public IMMPAIFInfo createMMPAIFInfo() {
+		return new MMPAIFInfo();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#createMMPResource()
+	 */
+	public IMMPResource createMMPResource() {
+		return new MMPResource();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getASSPLibraries()
+	 */
+	public List<String> getASSPLibraries() {
+		return stringListArgumentSettings.get(EMMPStatement.ASSPLIBRARY);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getAifInfo()
+	 */
+	public List<IMMPAIFInfo> getAifs() {
+		return aifs;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getMultiImageSources()
+	 */
+	public List<IMultiImageSource> getMultiImageSources() {
+		return (List) bitmaps;
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getBitmaps()
+	 */
+	public List<IMMPBitmap> getBitmaps() {
+		return bitmaps;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getDebugLibraries()
+	 */
+	public List<String> getDebugLibraries() {
+		return stringListArgumentSettings.get(EMMPStatement.DEBUGLIBRARY);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getFlags()
+	 */
+	public Set<EMMPStatement> getFlags() {
+		return new ValidatingSet<EMMPStatement>(flagSettings) {
+
+			public boolean isValidEntry(EMMPStatement entry) {
+				return isFlagSettingStatement(entry);
+			}
+			
+		};
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getLanguages()
+	 */
+	public List<EMMPLanguage> getLanguages() {
+		return languages;
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getLibraries()
+	 */
+	public List<String> getLibraries() {
+		return stringListArgumentSettings.get(EMMPStatement.LIBRARY);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getListArgumentSettings()
+	 */
+	public Map<EMMPStatement, List<String>> getListArgumentSettings() {
+		return new ValidatingKeyMap<EMMPStatement, List<String>>(stringListArgumentSettings) {
+
+			@Override
+			public boolean isAllowedKey(EMMPStatement key) {
+				return isStringListArgumentSettingStatement(key);
+			}
+			
+		};
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getMMPModel()
+	 */
+	public IMMPModel getMMPModel() {
+		return (IMMPModel) getModel();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getResources()
+	 */
+	public List<IMMPResource> getResourceBlocks() {
+		return resourceBlocks;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getSingleArgumentSettings()
+	 */
+	public Map<EMMPStatement, String> getSingleArgumentSettings() {
+		return new ValidatingKeyMap<EMMPStatement, String>(singleArgumentSettings) {
+
+			@Override
+			public boolean isAllowedKey(EMMPStatement key) {
+				return isSingleArgumentSettingStatement(key);
+			}
+			
+		};
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getEffectiveSourcePaths()
+	 */
+	public IPath[] getEffectiveSourcePaths() {
+		Set<IPath> sourcePaths = new LinkedHashSet<IPath>();
+		for (IPath source : sources) {
+			sourcePaths.add(source.removeLastSegments(1));
+		}
+		for (IPath source : userResources) {
+			sourcePaths.add(source.removeLastSegments(1));
+		}
+		for (IPath source : systemResources) {
+			sourcePaths.add(source.removeLastSegments(1));
+		}
+		for (IPath source : documents) {
+			sourcePaths.add(source.removeLastSegments(1));
+		}
+		return (IPath[]) sourcePaths.toArray(new IPath[sourcePaths.size()]);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getRealSourcePaths()
+	 */
+	public IPath[] getRealSourcePaths() {
+		return (IPath[]) sourcePaths.toArray(new IPath[sourcePaths.size()]); 
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getSources()
+	 */
+	public List<IPath> getSources() {
+		return sources;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getStaticLibraries()
+	 */
+	public List<String> getStaticLibraries() {
+		return stringListArgumentSettings.get(EMMPStatement.STATICLIBRARY);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getSystemIncludes()
+	 */
+	public List<IPath> getSystemIncludes() {
+		return systemIncludes;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getSystemResources()
+	 */
+	public List<IPath> getSystemResources() {
+		return systemResources;
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getTargetFilePath()
+	 */
+	public IPath getTargetFilePath() {
+		String name = singleArgumentSettings.get(EMMPStatement.TARGET);
+		if (name == null)
+			return null;
+		String dir = singleArgumentSettings.get(EMMPStatement.TARGETPATH);
+		if (dir == null)
+			return null;
+		IPath path = new Path(FileUtils.createPossiblyRelativePath(dir).append(name).toOSString());
+		if (FileUtils.isPathInParent(path)) {
+			path = getProjectPath().append(path);
+		}
+		return path;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getTargetType()
+	 */
+	public String getTargetType() {
+		return singleArgumentSettings.get(EMMPStatement.TARGETTYPE);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getUserIncludes()
+	 */
+	public List<IPath> getUserIncludes() {
+		return userIncludes;
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getUserResources()
+	 */
+	public List<IPath> getUserResources() {
+		return userResources;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getStaticLibraries()
+	 */
+	public List<String> getWin32Libraries() {
+		return stringListArgumentSettings.get(EMMPStatement.WIN32_LIBRARY);
+	}
+
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setAifInfo(com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPAIFInfo)
+	 */
+	public void setAifs(List<IMMPAIFInfo> aifs) {
+		Check.checkArg(aifs);
+		this.aifs = aifs;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setBitmaps(java.util.List)
+	 */
+	public void setBitmaps(List<IMMPBitmap> bitmaps) {
+		Check.checkArg(bitmaps);
+		this.bitmaps = bitmaps;
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setTargetFilePath(org.eclipse.core.runtime.IPath)
+	 */
+	public void setTargetFilePath(IPath path) {
+		path = path.removeTrailingSeparator();
+		Check.checkArg(path.segmentCount() > 1);
+		String filename = path.lastSegment();
+		IPath dir = path.removeLastSegments(1);
+		singleArgumentSettings.put(EMMPStatement.TARGETPATH, pathString(dir)); 
+		singleArgumentSettings.put(EMMPStatement.TARGET, filename); 
+				
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getUid2()
+	 */
+	public String getUid2() {
+		return uid2;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getUid3()
+	 */
+	public String getUid3() {
+		return uid3;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setUid2(java.lang.String)
+	 */
+	public void setUid2(String uid) {
+		this.uid2 = uid;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setUid2(int)
+	 */
+	public void setUid2(int value) {
+		this.uid2 = "0x" + Integer.toHexString(value); //$NON-NLS-1$
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setUid3(java.lang.String)
+	 */
+	public void setUid3(String uid) {
+		this.uid3 = uid;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setUid3(int)
+	 */
+	public void setUid3(int value) {
+		this.uid3 = "0x" + Integer.toHexString(value); //$NON-NLS-1$
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getLinkerOptions()
+	 */
+	public Map<String, String> getLinkerOptions() {
+		return linkerOptions;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getOptions()
+	 */
+	public Map<String, String> getOptions() {
+		return options;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getReplaceOptions()
+	 */
+	public Map<String, String> getReplaceOptions() {
+		return replaceOptions;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setLinkerOptions(java.util.Map)
+	 */
+	public void setLinkerOptions(Map<String, String> options) {
+		Check.checkArg(options);
+		this.linkerOptions = options;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setOptions(java.util.Map)
+	 */
+	public void setOptions(Map<String, String> options) {
+		Check.checkArg(options);
+		this.options = options;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#setReplaceOptions(java.util.Map)
+	 */
+	public void setReplaceOptions(Map<String, String> options) {
+		Check.checkArg(options);
+		this.replaceOptions = options;
+	}
+	
+	/**
+	 * Tell if the given macro is defined in the view configuration.
+	 * @param macro
+	 * @return
+	 */
+	public boolean isMacroDefined(String macro) {
+		for (IDefine define : getViewConfiguration().getMacros()) {
+			if (define.getName().equals(macro))
+				return true;
+		}
+		return false;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getDocuments()
+	 */
+	public List<IPath> getDocuments() {
+		return documents;
+	}
+	
+	/**
+	 * Track the statements known to the view from scanning.
+	 * These will be updated when the view is updated.  Any others
+	 * (i.e. illegal statements or unhandled ones) will be left alone.
+	 * @return
+	 */
+	public Collection<IASTStatement> getKnownStatements() {
+		return knownStatements;
+	}
+
+	public IPath getDefFile() {
+		IMMPViewConfiguration viewConfiguration = (IMMPViewConfiguration) getViewConfiguration();
+		String theDefFile = singleArgumentSettings.get(EMMPStatement.DEFFILE);;
+		return convertMMPDefFileToProjectOrFullPath(
+				theDefFile, isDefFileInFixedDirectory(), 
+				viewConfiguration.getDefaultDefFileBase(isASSP()),
+				viewConfiguration.isEmulatorBuild());
+	}
+	
+	public boolean isDefFileInFixedDirectory() {
+		String theDefFile = singleArgumentSettings.get(EMMPStatement.DEFFILE);
+		if (theDefFile == null)
+			return false;
+		return theDefFile.indexOf('/') >= 0 || theDefFile.indexOf('\\') >= 0;
+	}
+	
+	public void setDefFile(IPath path, boolean isFixedDirectory) {
+		// NOTE: we assume the new/modified statement always goes to the main file, otherwise postpone this
+		//Check.checkState(ModelSynchronizer.FORCE_NEW_CONTENT_TO_MAIN_DOCUMENT);
+		IMMPViewConfiguration viewConfiguration = (IMMPViewConfiguration) getViewConfiguration();
+		String newDefFile = convertPhysicalFileToMMPDefFile(path, 
+				viewConfiguration.getDefaultDefFileBase(isASSP()),
+				viewConfiguration.isEmulatorBuild(),
+				isFixedDirectory);
+		singleArgumentSettings.put(EMMPStatement.DEFFILE, newDefFile);
+		defFileBase = convertModelToProjectPath(new Path("")); // //$NON-NLS-1$
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.internal.cpp.epoc.engine.model.ViewBase#addViewSpecificMessages(java.util.List)
+	 */
+	@Override
+	protected void addViewSpecificMessages(final List messageList) {
+		// also add IASTMMPUnknownStatements as warnings
+		getFilteredTranslationUnit().accept(new IASTVisitor() {
+
+			public int visit(IASTNode node) {
+				if (node instanceof IASTMMPUnknownStatement) {
+					String text = node.getOriginalText();
+					if (text == null)
+						text = node.getNewText();
+					messageList.add(ASTMMPFactory.createMessage(
+									IMessage.WARNING,
+									"MMPView.UnknownStatement", //$NON-NLS-1$
+									new Object[] { text }, 
+									node.getMessageLocation()));
+					return IASTVisitor.VISIT_SIBLINGS;
+				} else if (node instanceof IASTMMPStatement
+						&& EMMPStatement.MESSAGE.matches((IASTMMPStatement) node)) {
+					IASTListNode<IASTLiteralTextNode> message = ((IASTMMPListArgumentStatement) node).getList();
+					String text = message.getOriginalText();
+					if (text == null)
+						text = message.getNewText();
+					messageList.add(ASTMMPFactory.createMessage(
+							IMessage.WARNING,
+							"MMPView.MessageStatement", //$NON-NLS-1$
+							new Object[] { text }, 
+							node.getMessageLocation()));
+					return IASTVisitor.VISIT_SIBLINGS;
+				}
+				return IASTVisitor.VISIT_CHILDREN;
+			}
+			
+		});
+		
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.internal.cpp.epoc.engine.model.ViewBase#getSpacingCategory(com.nokia.carbide.internal.api.cpp.epoc.engine.dom.IASTTopLevelNode)
+	 */
+	@Override
+	protected Object getSpacingCategory(IASTTopLevelNode node) {
+		if (!(node instanceof IASTMMPStatement) || (node instanceof IASTProblemNode))
+			return null;
+		try {
+			EMMPStatement stmt = EMMPStatement.valueOf(
+					((IASTMMPStatement) node).getKeywordName().toUpperCase());
+			if (stmt == EMMPStatement.SOURCEPATH
+					|| stmt == EMMPStatement.SOURCE
+					|| stmt == EMMPStatement.RESOURCE
+					|| stmt == EMMPStatement.SYSTEMRESOURCE
+					|| stmt == EMMPStatement.DOCUMENT) {
+				return EMMPStatement.SOURCEPATH.getCategory();
+			}
+			return stmt.getCategory();
+		} catch (IllegalArgumentException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Merge a list of statements, with the assumption that only one instance
+	 * of a given statement is retained.
+	 * @param intoStmts original statements
+	 * @param fromStmts ones to replace
+	 */
+	public void mergeStatementList(IASTListNode<IASTMMPStatement> intoStmts, IASTListNode<IASTMMPStatement> fromStmts) {
+		// unparent the fromStmts
+		for (IASTMMPStatement stmt : fromStmts)
+			stmt.setParent(null);
+		
+		for (ListIterator<IASTMMPStatement> iter = intoStmts.listIterator(); iter.hasNext(); ) {
+			IASTMMPStatement into = iter.next();
+			if (into instanceof IASTMMPProblemStatement)
+				continue;
+			
+			EMMPStatement stmt = null;
+			try {
+				stmt = EMMPStatement.valueOf(into.getKeywordName().toUpperCase());
+			} catch (IllegalArgumentException e) {
+				// ignore
+				continue;
+			}
+				
+			boolean handled = false;
+			for (ListIterator<IASTMMPStatement> iter2 = fromStmts.listIterator(); iter2.hasNext(); ) {
+				IASTMMPStatement from = iter2.next();
+				try {
+					EMMPStatement stmt2 = EMMPStatement.valueOf(from.getKeywordName().toUpperCase());
+					if (stmt == stmt2) {
+						// replace
+						iter.remove();
+						iter.add(from);
+						iter2.remove();
+						handled = true;
+						break;
+					}
+				} catch (IllegalArgumentException e2) {
+					// who knows?
+				}
+			}
+			
+			if (!handled) {
+				// the old statement is not replaced, so it must be removed
+				iter.remove();
+			}
+		}
+		
+		// now, take remaining fromStmts and add
+		intoStmts.addAll(fromStmts);
+	}
+
+	/** Helper method for now.  Returns list of unique source paths. */
+	Collection<IPath> getSourcePaths() {
+		return sourcePaths;
+	}
+	
+	/**
+	 * Convert the DEFFILE argument returned from IMMPView to a project-relative 
+	 * or absolute path.  If the path has no directory or no filename,
+	 * 'U' is added (for Unicode builds) and a platform-specific directory
+	 * ("bwins" or "eabi") is added as in makmake.
+	 * @param defFileSetting the DEFFILE setting from {@link IMMPView#getSingleArgumentSettings().get(EMMPStatement.DEFFILE)} -- may be null
+	 * to determine default filename
+	 * @param fixedDirectory 
+	 * @return a path derived from defFileSetting adjusted to the location of a .def file within a project
+	 * or a full path if outside the project, or null.  
+	 */
+	private IPath convertMMPDefFileToProjectOrFullPath(String defFileSetting, boolean fixedDirectory, String implDirectory, boolean isEmulator) {
+		String ext = null;
+		String baseName = null;
+		IPath dirPath = null;
+		
+		// no def file if not required
+		if (defFileSetting == null && !requiresDefFile())
+			return null;
+		
+		IPath mmpPath = defFileSetting != null ? FileUtils.createPossiblyRelativePath(defFileSetting) : null;
+		
+		if (mmpPath != null && !mmpPath.hasTrailingSeparator()) {
+			// filename is provided
+			ext = mmpPath.getFileExtension();
+			if (ext == null)
+				ext = DEF_FILE_EXTENSION;
+			IPath basePath = mmpPath.removeFileExtension();
+			baseName = basePath.lastSegment();
+			
+			if (basePath.isAbsolute()) {
+				dirPath = basePath.removeLastSegments(1);
+			} else {
+				dirPath = defFileBase.append(basePath.removeLastSegments(1));
+			}
+			//if (!dirPath.isAbsolute())
+			//	dirPath = ((IModel)mmpView.getModel()).getPath().removeLastSegments(1).append(dirPath);
+			
+		} else {
+			// no filename, so guess it from the target name
+			//if (mmpPath == null || !mmpPath.isAbsolute())
+			//	dirPath = ((IModel)mmpView.getModel()).getPath().removeLastSegments(1).append(mmpPath);
+			//else
+			if (mmpPath == null)
+				dirPath = new Path(""); //$NON-NLS-1$
+			else
+				dirPath = mmpPath;
+			dirPath = convertModelToProjectPath(dirPath);
+			String targetName = getSingleArgumentSettings().get(EMMPStatement.TARGET);
+			if (targetName == null)
+				targetName = "unnamed"; //$NON-NLS-1$ 
+			baseName = new Path(targetName).removeFileExtension().lastSegment();
+			ext = DEF_FILE_EXTENSION; 
+		}
+
+		// add implicit directory if needed
+		if (!fixedDirectory) {
+			if (implDirectory != null) {
+				dirPath = dirPath.append("..").append(implDirectory); //$NON-NLS-1$
+			}
+		} else {
+			// replace any "/~/" sequences 
+			if (dirPath.segmentCount() > 0)
+				dirPath = new Path(dirPath.addTrailingSeparator().toString().replace("/~/", "/"+implDirectory+"/")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+		
+		// version identifier overrides unicode
+		String versionString = getFileVersionString(); 
+		if (!isEmulator && versionString != null) {
+			baseName += versionString;
+		}
+		else if (!isNoStrictDef()) {
+			// unicode is on by default unless the user wants to explicitly set the name
+			baseName += "U"; //$NON-NLS-1$
+		}
+		
+		IPath prjPath = null;
+		IPath tempPath = dirPath.append(baseName + "." + ext); //$NON-NLS-1$
+		if (!tempPath.isAbsolute()) {
+			prjPath = tempPath; //convertMMPToProject(EMMPPathContext.DEFFILE, tempPath);
+		} else if (mmpPath == null || !mmpPath.isAbsolute()) {
+			/*
+			IPath wsPath = epocHelper.convertFilesystemToWorkspace(tempPath);
+			if (wsPath != null)
+				prjPath = wsPath.removeFirstSegments(1);
+			
+			if (prjPath == null) {
+				if (mmpView != null) {
+					IPath projectRoot = mmpView.getViewConfiguration().getViewParserConfiguration().getProjectLocation();
+					prjPath = FileUtils.removePrefixFromPath(projectRoot, tempPath);
+				}
+			}
+			*/
+		}
+		if (prjPath == null) {
+			// e.g. for full paths
+			prjPath = tempPath;
+		}
+		return prjPath;
+	}
+
+	private String getFileVersionString() {
+		List<String> versionArgs = getListArgumentSettings().get(EMMPStatement.VERSION);
+		if (versionArgs == null || versionArgs.size() < 1)
+			return null;
+		try {
+			Version version = new Version(versionArgs.get(0));
+			Formatter formatter = new Formatter();
+			formatter.format("{%04x%04x}", version.getMajor(), version.getMinor()); //$NON-NLS-1$
+			return formatter.toString();
+		} catch (IllegalArgumentException e) {
+			return null;
+		}
+	}
+
+
+	/**
+	 * Convert a def file path, which may have a 'U' added for a Unicode build,
+	 * and an implicit bwins or eabi directory, and remove them.  This may
+	 * return null if the DEFFILE statement is not needed.
+	 * <p>
+	 * This does not provide a complete mapping back to the appropriate DEFFILE
+	 * statement if the realPath was previously created from {@link #convertMMPDefFileToProjectOrFullPath(String)}
+	 * where the DEFFILE statement was missing.  The client should determine whether
+	 * a non-null result is sensible (e.g. points to a real file).
+	 * <p>
+	 * @param realPath the path to a real file; if relative, assumed project-relative, else assumed full path
+	 * @param implDirectory the directory for platform .def files or null for unknown
+	 * @param isEmulator true if an emulator build, else false: determines whether the version is added
+	 * @param isFixedDirectory true if the realPath should not be converted to platform-independent
+	 * @return a string adjusted to the MMP format.  It may be null.
+	 */
+	private String convertPhysicalFileToMMPDefFile(IPath realPath, String implDirectory, boolean isEmulator, boolean isFixedDirectory) {
+		if (realPath == null)
+			return null;
+
+		if (!realPath.isAbsolute()) {
+			//realPath = ((IModel)mmpView.getModel()).getPath().removeLastSegments(1).append(realPath);
+			realPath = convertProjectToModelPath(realPath);
+		}
+		
+		String ext = null;
+		String baseName = null;
+		IPath dirPath = null;
+		
+		ext = FileUtils.getSafeFileExtension(realPath);
+		IPath basePath = realPath.removeFileExtension();
+		baseName = basePath.lastSegment();
+		dirPath = basePath.removeLastSegments(1);
+
+		// remove Unicode or version suffix
+		if (!isNoStrictDef() && baseName.toUpperCase().endsWith("U")) { //$NON-NLS-1$
+			baseName = baseName.substring(0, baseName.length() - 1);
+		} else {
+			Matcher matcher = VERSION_PATTERN.matcher(baseName);
+			if (matcher.matches()) {
+				baseName = matcher.group(1);
+			}
+		}
+		
+		// remove implicit directory if present
+		if (!isFixedDirectory && implDirectory != null && dirPath.segmentCount() > 0 && dirPath.lastSegment().equalsIgnoreCase(implDirectory)) {
+			dirPath = dirPath.removeLastSegments(2);	// ".." and impl
+		}
+
+		// return string value, containing literal .\ if not generic name
+		String mmpPath = pathString(dirPath.append(baseName + "." + ext)); //$NON-NLS-1$
+		if (isFixedDirectory && !dirPath.isAbsolute() && dirPath.segmentCount() == 0)
+			mmpPath = "." + pathSeparator() + mmpPath; //$NON-NLS-1$
+		return mmpPath;
+	}
+
+	/**
+	 * Tell whether NOSTRICTDEF was defined in the MMP view, e.g.,
+	 * if the view does not change the name of DEFFILEs for Unicode
+	 * builds.
+	 * @return flag
+	 */
+	private boolean isNoStrictDef() {
+		return getFlags().contains(EMMPStatement.NOSTRICTDEF);
+	}
+
+	/**
+	 * Tell whether the ASSP format exports are used
+	 * @return flag
+	 */
+	private boolean isASSP() {
+		return getFlags().contains(EMMPStatement.ASSPEXPORTS);
+	}
+
+	/**
+	 * Tell whether the MMP file requires a .def file, which tells whether
+	 * a missing DEFFILE statement has an implicit one.
+	 * @return flag
+	 */
+	private boolean requiresDefFile() {
+		String targetType = getSingleArgumentSettings().get(EMMPStatement.TARGETTYPE);
+		if (targetType == null)
+			return false;
+		return targetType.toUpperCase().matches("DLL|EXEDLL|EXEXP|IMPLIB|STDDLL"); //$NON-NLS-1$
+	}
+
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPView#getData()
+	 */
+	public IMMPData getData() {
+		return new MMPData(this);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.nokia.carbide.cpp.epoc.engine.model.mmp.IMMPData#getModelPath()
+	 */
+	public IPath getModelPath() {
+		return model.getPath();
+	}
+}