uidesigner/com.nokia.sdt.sourcegen/src/com/nokia/sdt/sourcegen/core/ResourceTracker.java
changeset 0 fb279309251b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/uidesigner/com.nokia.sdt.sourcegen/src/com/nokia/sdt/sourcegen/core/ResourceTracker.java	Fri Apr 03 23:33:03 2009 +0100
@@ -0,0 +1,559 @@
+/*
+* Copyright (c) 2005 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.sdt.sourcegen.core;
+
+import com.nokia.cpp.internal.api.utils.core.Check;
+import com.nokia.cpp.internal.api.utils.core.ObjectUtils;
+import com.nokia.cpp.internal.api.utils.core.TextUtils;
+import com.nokia.sdt.datamodel.IDesignerDataModel;
+import com.nokia.sdt.datamodel.adapter.IComponentInstance;
+import com.nokia.sdt.emf.dm.*;
+import com.nokia.sdt.sourcegen.doms.rss.IRssModelGenerator;
+import com.nokia.sdt.sourcegen.doms.rss.dom.*;
+import com.nokia.sdt.workspace.IDesignerDataModelSpecifier;
+
+import org.eclipse.core.runtime.IStatus;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+/**
+ * Track resources generated during source mapping for a single model.
+ * This does not include any information about resources
+ * existing in RSS created by the user.
+ * 
+ *
+ */
+public class ResourceTracker {
+	
+    /**
+     * This class tracks a generated resource definition or expression.
+     * It can track the eventual name of a resource definition,
+     * an incomplete resource definition, or a complete resource
+     * definition.  Finally, it can represent a resource expression,
+     * which may be used to prevent emitting a resource definition
+     * if the only reason is completeness.
+     * <p>
+     * The existence of resource info serves two purposes:<br>
+     * 1) Tell that a given named resource definition is already
+     * present for a given InstanceInfo,<br>
+     * and 2) 
+     *  
+     * 
+     *
+     */
+    public static class ResourceInfo {
+        /** resource name for statement (or null), or null for expression */
+        String name;
+        /** tell what rsrcFile attribute the mapResource says this lives in (or null) */
+        private String rsrcFile;
+        /** tell whether a resource is complete (if set, implies rsrc!= null) */
+        boolean complete;
+
+        /** Create info for an unknown resource */
+        public ResourceInfo() {
+            this.name = null;
+            this.complete = false;
+            this.rsrcFile = null;
+        }
+
+        /** Create info for an ungenerated resource */
+        public ResourceInfo(String name) {
+            this.name = name;
+            this.complete = false;
+        }
+        
+        /** Create info for a generated resource */
+        public ResourceInfo(IAstResourceDefinition rsrc, boolean defaultLocation) {
+        	setResourceDefinition(rsrc, defaultLocation);
+        }
+        
+        public ResourceInfo(ResourceInfo other) {
+            this.name = other.name;
+            this.complete = other.complete;
+            this.rsrcFile = other.rsrcFile;
+        }
+        
+        @Override
+        public String toString() {
+        	return "[name="+name+", rsrcFile="+rsrcFile+", complete="+complete+"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+        }
+        public void setName(String name) {
+            Check.checkArg(name);
+            this.name = name;
+        }
+        
+        public void setResourceDefinition(IAstResourceDefinition rsrc, boolean defaultFile) {
+            Check.checkArg(rsrc);
+            //this.rsrc = rsrc;
+            this.name = rsrc.getName() != null ? rsrc.getName().getName() : null;
+            if (defaultFile)
+            	this.rsrcFile = null;
+            else
+            	this.rsrcFile = rsrc.getAstSourceFile().getSourceFile().getFileName();
+        }
+        
+        public String getName() {
+            return name;
+        }
+        
+        public void markComplete() {
+            this.complete = true;
+        }
+        
+        public boolean isComplete() {
+            return complete;
+        }
+        
+        /**
+         * Reset info that refers to an expression so that
+         * a resource definition is emitted
+         */
+        public void markIncomplete() {
+            complete = false;
+        }
+
+		/**
+		 * Find the existing resource definition
+		 * @param rssGenerator
+		 * @return
+		 */
+		public IAstResourceDefinition findResourceDefinition(IRssModelGenerator rssGenerator) {
+			Check.checkArg(rssGenerator);
+			if (name == null)
+				return null;
+			ITranslationUnit tu = rssGenerator.createOrLoadTranslationUnit(rsrcFile, false);
+			if (tu == null)
+				return null;
+			return tu.findResourceDefinition(name);
+		}
+    }
+    
+    class InstanceInfo {
+    	String instanceName;
+    	String rsrcId;
+		public IDesignerDataModelSpecifier dmSpec;
+
+		public InstanceInfo(IComponentInstance instance, String rsrcId) {
+			Check.checkArg(instance);
+			this.instanceName = instance.getName();
+	        this.rsrcId = rsrcId;
+	        this.dmSpec = instance.getDesignerDataModel().getModelSpecifier();
+		}
+		
+		public InstanceInfo(IComponentInstance instance) {
+			this(instance, null);
+		}
+
+		public InstanceInfo(String instanceName, String rsrcId, IDesignerDataModelSpecifier dmSpec) {
+			Check.checkArg(instanceName);
+			this.instanceName = instanceName;
+	        this.rsrcId = rsrcId;
+	        this.dmSpec = dmSpec;
+		}
+
+		/** Copy instance info. */
+		public InstanceInfo(InstanceInfo other) {
+			this.instanceName = other.instanceName;
+	        this.rsrcId = other.rsrcId;
+	        this.dmSpec = other.dmSpec;
+		}
+
+        @Override
+        public String toString() {
+        	return "[instanceName="+instanceName+", rsrcId="+rsrcId+", dmSpec="+dmSpec+"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+        }
+
+    	private boolean isSameModel(IDesignerDataModel model) {
+    		// TODO: comparison of names works for now but is imprecise; fix TestDesignerDataModel so it works with paths
+    		if (dmSpec == null || model.getModelSpecifier() == null)
+    			return true;
+    		return dmSpec.getDisplayName().equals(model.getModelSpecifier().getDisplayName());
+    		//return dmSpec.getPrimaryResourcePath().equals(model.getModelSpecifier().getPrimaryResourcePath());
+    	}
+
+       	private boolean isSameModelSpecifier(IDesignerDataModelSpecifier dmSpec) {
+    		// TODO: comparison of names works for now but is imprecise; fix TestDesignerDataModel so it works with paths
+    		if (this.dmSpec == null || dmSpec == null)
+    			return true;
+    		return this.dmSpec.getDisplayName().equals(dmSpec.getDisplayName());
+       		//return this.dmSpec.getPrimaryResourcePath().equals(dmSpec.getPrimaryResourcePath());
+    	}
+
+		/* (non-Javadoc)
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		@Override
+		public boolean equals(Object obj) {
+			if (obj == this)
+				return true;
+			if (obj instanceof InstanceInfo) {
+				InstanceInfo other = (InstanceInfo) obj;
+				return other.instanceName.equals(instanceName) &&
+				ObjectUtils.equals(other.rsrcId, rsrcId) &&
+				isSameModelSpecifier(other.dmSpec);
+			}
+			return false;
+		}
+		
+		/* (non-Javadoc)
+		 * @see java.lang.Object#hashCode()
+		 */
+		@Override
+		public int hashCode() {
+			return instanceName.hashCode() 
+			^ (rsrcId != null ? rsrcId.hashCode() << 16 : 0)
+			^ (dmSpec != null ? dmSpec.getDisplayName().hashCode() : 0);
+		}
+    }
+    
+    /** The map of IComponentInstance names + facets to ResourceInfo */
+    private Map<InstanceInfo, ResourceInfo> instanceFacetToResourceInfoMap;
+
+    /**
+     * 
+     */
+    public ResourceTracker() {
+        this.instanceFacetToResourceInfoMap = new HashMap<InstanceInfo, ResourceInfo>();
+    }
+
+    /**
+     * 
+     */
+    public void reset() {
+        instanceFacetToResourceInfoMap.clear();
+    }
+    
+
+    /**
+     * Find or create ResourceInfo by the instance generating it,
+     * the &lt;mapResource&gt; facet that declares it,
+     * and the rsrcId from that facet.
+     * <p>
+     * The facet is used to distinguish multiple unnamed
+     * resources generated by the same instance, 
+     * for the purpose of ensuring each resource gets
+     * emitted.  (Otherwise 'instanceName' is the key for
+     * the first resource and the second isn't generated.)
+     * 
+     * @param instance the instance generating the resource
+     * @param rsrcId the identifier of the resource for the instance, may be null
+     * @param facet the MapResourceType facet
+     * @return ResourceInfo or null
+     */
+    public ResourceInfo findOrCreateResourceInfo(IComponentInstance instance, 
+            String rsrcId) {
+        InstanceInfo iinfo_f = new InstanceInfo(instance, rsrcId);
+        ResourceInfo info = instanceFacetToResourceInfoMap.get(iinfo_f);
+        if (info == null) {
+            info = new ResourceInfo();
+            instanceFacetToResourceInfoMap.put(iinfo_f, info);
+        }
+        //System.out.println("for instance " + instance + " w/rsrcid="+rsrcId +" w/facetid="+facetId
+        //		+", got info.name="+info.name+" & info.rsrc="+info.rsrc + " ==> " + info);
+        return info;
+    }
+
+
+    /**
+     * Find ResourceInfo by the instance generating it
+     * and the rsrcId associated with it.  Prefer any
+     * info that has a name over an expression.
+     * 
+     * @param instance the instance generating the resource
+     * @param rsrcId the identifier of the resource for the instance, may be null
+     * @return ResourceInfo or null
+     */
+    public ResourceInfo findResourceInfo(IComponentInstance instance, 
+            String rsrcId) {
+    	String instanceName = instance.getName();
+    	if (instanceName == null)
+    		return null;
+    	
+    	IDesignerDataModel model = instance.getDesignerDataModel();
+    	ResourceInfo last = null;
+    	for (Iterator<Map.Entry<InstanceInfo, ResourceInfo>> iter = 
+    		instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+    		Map.Entry<InstanceInfo, ResourceInfo> entry = iter.next();
+    		ResourceInfo info = entry.getValue();
+    		if (entry.getKey().instanceName.equals(instanceName)
+    			&& ObjectUtils.equals(entry.getKey().rsrcId, rsrcId)
+    			&& entry.getKey().isSameModel(model)) {
+    			if (last == null || last.getName() == null) 
+    				last = info;
+    		}
+		}
+    	
+    	//if (last != null)
+    	// System.out.println("found rsrcinfo " + last + " ("+last.name + "," +last.rsrc +") " 
+    	//		+ " for " + instance + " w/rsrcid=" + rsrcId);
+        return last;
+    }
+
+    /**
+     * Find ResourceInfo by the name of a resource.
+     * This can return external resources.
+     * @param rsrcName
+     * @return ResourceInfo or null
+     */
+    public ResourceInfo findResourceInfoByResourceName(String rsrcName) {
+        for (Iterator iter = instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+			Map.Entry<InstanceInfo, ResourceInfo> entry = (Map.Entry<InstanceInfo, ResourceInfo>) iter.next();
+			ResourceInfo info = (ResourceInfo) entry.getValue();
+			if (info.getName() != null && info.getName().equals(rsrcName))
+				return info;
+        }
+        return null;
+    }
+
+	/**
+	 * Merge data from another tracker into this one.
+	 * This introduces external resources into the map.
+	 * @param tracker
+	 */
+	public void merge(ResourceTracker tracker) {
+		for (Iterator iter = tracker.instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+			Map.Entry<InstanceInfo, ResourceInfo> entry = (Map.Entry<InstanceInfo, ResourceInfo>) iter.next();
+			registerMapping(entry.getKey(), entry.getValue());
+		}
+	}
+
+    /**
+     * Look up resource info for the given resource.
+     * This tells whether an existing resource is known or unknown.
+     * @param def
+     * @return
+     */
+    public ResourceInfo findInfoForResource(IAstResourceDefinition rsrc) {
+        Check.checkArg(rsrc);
+        Check.checkArg(rsrc.getName());
+        for (Iterator iter = instanceFacetToResourceInfoMap.values().iterator(); iter.hasNext();) {
+            ResourceInfo info = (ResourceInfo) iter.next();
+            if (ObjectUtils.equals(info.getName(), rsrc.getName())
+            		&& (info.rsrcFile == null 
+       				|| (rsrc.getAstSourceFile().getSourceFile().getFileName().equals(info.rsrcFile)))
+            	)
+                return info;
+        }
+        return null;
+    }
+
+    /**
+     *  Reset the "isComplete" flags on the generated resources,
+     *  so they will be updated. 
+     */
+    public void resetComplete() {
+        for (Iterator iter = instanceFacetToResourceInfoMap.values().iterator(); iter.hasNext();) {
+            ResourceInfo info = (ResourceInfo) iter.next();
+            info.complete = false;
+        }
+    }
+
+	/**
+	 * Return all the resources associated with the instance
+	 * @param instance
+	 * @return list of matching ResourceInfo
+	 */
+	public ResourceInfo[] getResourceInfos(IComponentInstance instance) {
+		return getResourceInfos(instance.getName());
+	}
+
+	/**
+	 * Return all the resources associated with the instance
+	 * @param instanceName
+	 * @return list of matching ResourceInfo
+	 */
+	public ResourceInfo[] getResourceInfos(String instanceName) {
+		List<ResourceInfo> list = new ArrayList<ResourceInfo>();
+        Check.checkArg(instanceName);
+        for (Iterator<Map.Entry<InstanceInfo, ResourceInfo>> iter = instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+        	Map.Entry<InstanceInfo, ResourceInfo> entry = iter.next();
+            if (entry.getKey().instanceName.equals(instanceName))
+                list.add(entry.getValue());
+        }
+        return (ResourceInfo[]) list.toArray(new ResourceInfo[list.size()]);
+	}
+	
+	/**
+	 * Forget we generated resources for the given instance
+	 */
+	public void removeResourceInfo(String instanceName) {
+        for (Iterator<Map.Entry<InstanceInfo, ResourceInfo>> iter = instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+        	Map.Entry<InstanceInfo, ResourceInfo> entry = iter.next();
+            if (entry.getKey().instanceName.equals(instanceName))
+                iter.remove();
+        }
+	}
+
+	/**
+	 * Save the mapping information for the data model
+	 */
+	public void saveState(IDesignerDataModel dataModel, ISourceGenMappingState state, IRssModelGenerator rssGenerator) {
+		IResourceMappings mappings = DmFactory.eINSTANCE.createIResourceMappings();
+		for (Iterator iter = instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+			Map.Entry<InstanceInfo, ResourceInfo> entry = (Map.Entry<InstanceInfo, ResourceInfo>) iter.next();
+			ResourceInfo rsrcInfo = entry.getValue();
+			InstanceInfo instanceInfo = entry.getKey();
+			// bug 4813: don't emit ungenerated resource (may be implicitly referenced
+			// by having been previously generated, but if it's no longer complete,
+			// then excepting a bug, it shouldn't be re-reported)
+			if (instanceInfo.isSameModel(dataModel) && rsrcInfo.name != null && rsrcInfo.isComplete()) {
+				// this is used to ensure we're not emitting 
+				// resources associated with the wrong model
+				Check.checkState(dataModel.findByNameProperty(instanceInfo.instanceName) != null);
+
+				IResourceMapping mapping = DmFactory.eINSTANCE.createIResourceMapping();
+				mapping.setInstanceName(instanceInfo.instanceName);
+				mapping.setRsrcId(instanceInfo.rsrcId);
+				mapping.setRsrcFile(rsrcInfo.rsrcFile);
+				mapping.setValue(rsrcInfo.name);
+
+				// check sanity
+				IAstRssSourceFile rssFile = rssGenerator.getRssFile(mapping.getRsrcFile(), false);
+				IAstResourceDefinition def = rsrcInfo.findResourceDefinition(rssGenerator);
+				if (def == null) {
+					// this isn't fatal but it's a problem
+					Messages.emit(IStatus.ERROR, rssFile.getSourceFile(), 
+							"ResourceTracker.DidNotFindResource", //$NON-NLS-1$
+									new Object[] { mapping.getValue(), rssFile.getSourceFile().getFile() });
+					
+				}
+				
+				mappings.getMappings().add(mapping);
+			}
+		}
+		
+		state.setResourceMappings(mappings);
+	}
+
+	/**
+	 * Load mapping information
+	 * @param model the model to load from (null means other model)
+	 * @param state state to read
+	 * @param tu the other model's RSS
+	 */
+	public void loadState(IDesignerDataModel dataModel, ISourceGenMappingState state, IRssModelGenerator generator) {
+		Check.checkArg(state);
+		Check.checkArg(generator);
+		if (state.getResourceMappings() == null)
+			return;
+		IDesignerDataModelSpecifier dmSpec = dataModel.getModelSpecifier();
+		for (Iterator iter = state.getResourceMappings().getMappings().iterator(); iter.hasNext();) {
+			IResourceMapping mapping = (IResourceMapping) iter.next();
+			if (TextUtils.strlen(mapping.getInstanceName()) > 0
+					//&& TextUtils.strlen(mapping.getRsrcId()) > 0
+					&& TextUtils.strlen(mapping.getValue()) > 0) {
+				ITranslationUnit tu = generator.createOrLoadTranslationUnit(mapping.getRsrcFile(), false);
+				IAstRssSourceFile rssFile = generator.getRssFile(mapping.getRsrcFile(), false);
+				if (!generator.getSourceGenProvider().getFileTracker().fileExists(rssFile.getSourceFile().getFile())) {
+					// the file does not exist: don't complain
+					iter.remove();
+					continue;
+				}
+					
+				IAstResourceDefinition rsrc = null;
+				rsrc = tu.findResourceDefinition(mapping.getValue());
+				if (rsrc != null) {
+					if (dataModel.findByNameProperty(mapping.getInstanceName()) == null) {
+						Messages.emit(IStatus.ERROR, rssFile.getSourceFile(), 
+								"ResourceTracker.DidNotFindInstance", //$NON-NLS-1$
+										new Object[] { mapping.getInstanceName(), dataModel.getModelSpecifier() });
+						iter.remove();
+					} else {
+						registerMapping(mapping, rsrc, dmSpec);
+					}
+				} else {
+					Messages.emit(IStatus.ERROR, rssFile.getSourceFile(), 
+							"ResourceTracker.DidNotFindResource", //$NON-NLS-1$
+									new Object[] { mapping.getValue(), rssFile.getSourceFile().getFile() });
+					iter.remove();
+				}
+			} else {
+				Messages.emit(IStatus.ERROR, dataModel, 
+						"ResourceTracker.IllegalResourceMapping", //$NON-NLS-1$
+								new Object[] { mapping  });
+				iter.remove();
+			}
+		}
+	}
+
+	/**
+	 * Register a resource mapping.
+	 * @param mapping
+	 */
+	public void registerMapping(IResourceMapping mapping, IAstResourceDefinition rsrc, IDesignerDataModelSpecifier dmSpec) {
+		Check.checkArg(rsrc);
+		InstanceInfo key = new InstanceInfo(mapping.getInstanceName(), mapping.getRsrcId(), dmSpec);
+		ResourceInfo value = new ResourceInfo(rsrc, TextUtils.strlen(mapping.getRsrcFile()) == 0);
+		instanceFacetToResourceInfoMap.put(key, value);
+	}
+
+	/**
+	 * Register info from another tracker.  The new entries are marked external.
+	 * @param mapping
+	 */
+	public void registerMapping(InstanceInfo instanceInfo, ResourceInfo resourceInfo) {
+		InstanceInfo key = new InstanceInfo(instanceInfo);
+		ResourceInfo value = new ResourceInfo(resourceInfo);
+		instanceFacetToResourceInfoMap.put(key, value);
+	}
+
+	/**
+	 * Reset info for the given file
+	 * @param fileName null for main file or named file
+	 */
+	public void resetForRssFile(String fileName) {
+		for (Iterator iter = instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+			Map.Entry<InstanceInfo, ResourceInfo> entry = (Map.Entry<InstanceInfo, ResourceInfo>) iter.next();
+			if (ObjectUtils.equals(entry.getValue().rsrcFile, fileName))
+				iter.remove();
+		}
+	}
+
+	/**
+	 * Get all the referenced instances.  Only non-external resources are returned.
+	 */
+	public String[] getReferencedInstances(IDesignerDataModel model) {
+		IDesignerDataModelSpecifier dmSpec = model.getModelSpecifier();
+		Set<String> names = new HashSet<String>();
+		for (Iterator iter = instanceFacetToResourceInfoMap.keySet().iterator(); iter.hasNext();) {
+			InstanceInfo info = (InstanceInfo) iter.next();
+			if (info.isSameModelSpecifier(dmSpec))
+				names.add(info.instanceName);
+		}
+		return (String[]) names.toArray(new String[names.size()]);
+	}
+
+	/**
+	 * Get list of generated resources.  This excludes external resources.
+	 * @return
+	 */
+	public Set<IAstResourceDefinition> getGeneratedResources(IRssModelGenerator generator, IDesignerDataModel model) {
+		Set<IAstResourceDefinition> rsrcs = new HashSet<IAstResourceDefinition>();
+		for (Iterator iter = instanceFacetToResourceInfoMap.entrySet().iterator(); iter.hasNext();) {
+			Map.Entry<InstanceInfo, ResourceInfo> entry = (Entry<InstanceInfo, ResourceInfo>) iter.next();
+			if (entry.getKey().isSameModel(model)) {
+				ResourceInfo rsrcInfo = entry.getValue();
+				IAstResourceDefinition def = rsrcInfo.findResourceDefinition(generator);
+				if (def != null)
+					rsrcs.add(def);
+			}
+		}
+		return rsrcs;
+	}
+}