/** * CertWare Project * Copyright (c) 2010 National Aeronautics and Space Administration. All rights reserved. */ package net.certware.sacm.navigator; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import net.certware.core.ICertWareConstants; import net.certware.core.ui.log.CertWareLog; import net.certware.sacm.SACM.Annotation; import net.certware.sacm.SACM.AssuranceCase; import net.certware.sacm.SACM.Datetime; import net.certware.sacm.SACM.ModelElement; import net.certware.sacm.SACM.SACMElement; import net.certware.sacm.SACM.TaggedValue; import net.certware.sacm.SACM.UtilityElement; import net.certware.sacm.SACM.util.SACMSwitch; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.util.URI; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.ui.progress.UIJob; /** * Provides model elements as navigator content. * Uses a visitor to read the model and accumulate counter values. * @author mrb */ public class ContentProvider implements ITreeContentProvider, IResourceChangeListener, IResourceDeltaVisitor, ICertWareConstants { /** initial capacity of tree map */ private static final int INITIAL_CAPACITY = 3; /** load factor for tree map */ private static final float LOAD_FACTOR = 0.75f; /** no children tree */ private static final Object[] NO_CHILDREN = new Object[0]; /** cached model for refresh */ private final Map<IFile, TreeData[]> cachedModelMap = new HashMap<IFile, TreeData[]>(INITIAL_CAPACITY,LOAD_FACTOR); /** tree viewer to update */ private StructuredViewer viewer; /** model utility elements */ private int utilityElementCount; /** model annotations */ private int annotationCount; /** model assurance cases */ private int assuranceCaseCount; /** model date time elements */ private int dateTimeCount; /** model elements */ private int modelElementCount; /** model SACM elements */ private int sacmElementCount; /** model tagged values */ private int taggedValueCount; /** * Constructor adds resource change listener for post change events. */ public ContentProvider() { ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); } /** * Disposes of content provider, clears map, and removes resource change listener. * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @Override public void dispose() { cachedModelMap.clear(); ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } /** * Responds to content input change for the provider. * @param aViewer structured content viewer * @param oldInput previous input model * @param newInput new input model * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, Object, Object) */ @Override public void inputChanged(Viewer aViewer, Object oldInput, Object newInput) { if (null != oldInput && !oldInput.equals(newInput)) { cachedModelMap.clear(); } viewer = (StructuredViewer) aViewer; } /** * Visitor for resource change deltas. * @param delta resource change delta * @return false for file type resource processing, true otherwise * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(IResourceDelta) */ @Override public boolean visit(IResourceDelta delta) { final IResource source = delta.getResource(); switch (source.getType()) { case IResource.ROOT: case IResource.PROJECT: case IResource.FOLDER: return true; case IResource.FILE: final IFile file = (IFile) source; if ( isSacm(file) ) { updateModel(file); new UIJob(Messages.Job) { public IStatus runInUIThread(IProgressMonitor monitor) { if (null != viewer ) { // && ! viewer.getControl().isDisposed()) viewer.refresh(file); } return Status.OK_STATUS; } }.schedule(); } return false; default: break; } return false; } /** * Method resourceChanged. * @param event IResourceChangeEvent * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(IResourceChangeEvent) */ @Override public void resourceChanged(IResourceChangeEvent event) { final IResourceDelta delta = event.getDelta(); try { delta.accept(this); } catch (CoreException e) { CertWareLog.logError(Messages.ResourceChanged,e); } } /** * Gets the input model children as elements. * @param inputElement input model whose children are returned * @return Object[] children * @see org.eclipse.jface.viewers.ITreeContentProvider#getElements(Object) */ @Override public Object[] getElements(Object inputElement) { return getChildren(inputElement); } /** * Gets the children of the given parent element. * @param parentElement parent element * @return Object[] of NO_CHILDREN if parent is a TreeData object, otherwise cached values if IFile object * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(Object) */ @Override public Object[] getChildren(Object parentElement) { Object[] children = null; if ( parentElement instanceof TreeData ) { children = NO_CHILDREN; } else if (parentElement instanceof IFile) { final IFile modelFile = (IFile)parentElement; if ( isSacm(modelFile) ) { children = cachedModelMap.get(modelFile); if ( null == children && null != updateModel(modelFile) ) { children = cachedModelMap.get(modelFile); } } } return (null != children) ? children : NO_CHILDREN; } /** * Gets the parent TreeData object if element is TreeData, otherwise null. * @param element TreeData element to find IFile * @return TreeData object IFile, otherwise TRUE if element is not a tree data object * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(Object) */ @Override public Object getParent(Object element) { if ( element instanceof TreeData ) { final TreeData td = (TreeData)element; return td.getIfile(); } return Boolean.TRUE; } /** * Whether a given file has an extension matching the SACM models. * @param f file to text extension * @return true if extension matches one of several SACM extensions */ private boolean isSacm(IFile f) { String ext = f.getFileExtension(); return ICertWareConstants.SACM_EXTENSION.equals(ext) || ICertWareConstants.SACM_ARG_EXTENSION.equals(ext) || ICertWareConstants.SACM_EVIDENCE_EXTENSION.equals(ext); } /** * Method hasChildren. * @param element Object * @return boolean * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(Object) */ @Override public boolean hasChildren(Object element) { if ( element instanceof TreeData ) { return false; } else if ( element instanceof IFile ) { return isSacm((IFile)element); } return false; } /** * Updates the navigator model from the element statistics in the resource model file. * @param modelFile input file * @return model resource processed from file */ private synchronized Resource updateModel(IFile modelFile) { if ( isSacm(modelFile) ) { if (modelFile.exists()) { final ResourceSet resourceSet = new ResourceSetImpl(); final Resource resource = resourceSet.getResource( URI.createPlatformResourceURI(modelFile.getFullPath().toString(), true), true); if ( null != resource ) { setAnnotationCount(0); setAssuranceCaseCount(0); setDateTimeCount(0); setModelElementCount(0); setSacmElementCount(0); setTaggedValueCount(0); setUtilityElementCount(0); // visit the model, collect statistics for ( Iterator<EObject> i = resource.getAllContents(); i.hasNext(); ) { EObject eo = i.next(); visitor.doSwitch(eo); } final List<TreeData> treeNodes = new ArrayList<TreeData>(); treeNodes.add(new TreeData(modelFile,Messages.ContentProvider_0,getAnnotationCount(),TreeData.COUNT_TYPE_ANNOTATIONS)); treeNodes.add(new TreeData(modelFile,Messages.ContentProvider_1,getAssuranceCaseCount(),TreeData.COUNT_TYPE_ASSURANCE_CASES)); treeNodes.add(new TreeData(modelFile,Messages.ContentProvider_2,getDateTimeCount(),TreeData.COUNT_TYPE_DATE_TIMES)); treeNodes.add(new TreeData(modelFile,Messages.ContentProvider_3,getModelElementCount(),TreeData.COUNT_TYPE_MODEL_ELEMENTS)); treeNodes.add(new TreeData(modelFile,Messages.ContentProvider_4,getSacmElementCount(),TreeData.COUNT_TYPE_SACM_ELEMENTS)); treeNodes.add(new TreeData(modelFile,Messages.ContentProvider_5,getTaggedValueCount(),TreeData.COUNT_TYPE_TAGGED_VALUES)); treeNodes.add(new TreeData(modelFile,Messages.ContentProvider_6,getTaggedValueCount(),TreeData.COUNT_TYPE_UTILITY_ELEMENTS)); // populate array and model map final TreeData[] treeDataArray = treeNodes.toArray(new TreeData[treeNodes.size()]); cachedModelMap.put(modelFile, treeDataArray); return resource; } // model not null } // model file exists } // file extension matches cachedModelMap.remove(modelFile); return null; } // method public void setUtilityElementCount(int elementCount) { this.utilityElementCount = elementCount; } public int getUtilityElementCount() { return utilityElementCount; } public void incrementUtilityElement() { utilityElementCount += 1; } public void incrementAnnotationCount() { annotationCount += 1; } public void setAnnotationCount(int count) { annotationCount = count; } public int getAnnotationCount() { return annotationCount; } public void incrementAssuranceCaseCount() { assuranceCaseCount += 1; } public void setAssuranceCaseCount(int count) { assuranceCaseCount = count; } public int getAssuranceCaseCount() { return assuranceCaseCount; } public void incrementDateTimeCount() { dateTimeCount += 1; } public void setDateTimeCount(int count) { dateTimeCount = count; } public int getDateTimeCount() { return dateTimeCount; } public void incrementModelElementCount() { modelElementCount += 1; } public void setModelElementCount(int count) { modelElementCount = count; } public int getModelElementCount() { return modelElementCount; } public void incrementSacmElementCount() { sacmElementCount += 1; } public void setSacmElementCount(int count) { sacmElementCount = count; } public int getSacmElementCount() { return sacmElementCount; } public void incrementTaggedValueCount() { taggedValueCount += 1; } public void setTaggedValueCount(int count) { taggedValueCount = count; } public int getTaggedValueCount() { return taggedValueCount; } public void incrementUtilityElementCount() { utilityElementCount += 1; } /** * Visitor to pass over model elements and links. */ public SACMSwitch<Boolean> visitor = new SACMSwitch<Boolean>() { /** * Returns the result of interpreting the object as an instance of '<em>Annotation</em>'. * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>Annotation</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public Boolean caseAnnotation(Annotation object) { incrementAnnotationCount(); return Boolean.TRUE; } /** * Returns the result of interpreting the object as an instance of '<em>Assurance Case</em>'. * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>Assurance Case</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public Boolean caseAssuranceCase(AssuranceCase object) { incrementAssuranceCaseCount(); return Boolean.TRUE; } /** * Returns the result of interpreting the object as an instance of '<em>Datetime</em>'. * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>Datetime</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public Boolean caseDatetime(Datetime object) { incrementDateTimeCount(); return Boolean.TRUE; } /** * Returns the result of interpreting the object as an instance of '<em>Model Element</em>'. * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>Model Element</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public Boolean caseModelElement(ModelElement object) { incrementModelElementCount(); return Boolean.TRUE; } /** * Returns the result of interpreting the object as an instance of '<em>Element</em>'. * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>Element</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public Boolean caseSACMElement(SACMElement object) { incrementSacmElementCount(); return Boolean.TRUE; } /** * Returns the result of interpreting the object as an instance of '<em>Tagged Value</em>'. * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>Tagged Value</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public Boolean caseTaggedValue(TaggedValue object) { incrementTaggedValueCount(); return Boolean.TRUE; } /** * Returns the result of interpreting the object as an instance of '<em>Utility Element</em>'. * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>Utility Element</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) doSwitch(EObject) */ public Boolean caseUtilityElement(UtilityElement object) { incrementUtilityElement(); return Boolean.TRUE; } /** * Returns the result of interpreting the object as an instance of '<em>EObject</em>'. * <!-- begin-user-doc --> * This implementation returns null; * returning a non-null result will terminate the switch, but this is the last case anyway. * <!-- end-user-doc --> * @param object the target of the switch. * @return the result of interpreting the object as an instance of '<em>EObject</em>'. * @see #doSwitch(org.eclipse.emf.ecore.EObject) */ @Override public Boolean defaultCase(EObject object) { return Boolean.TRUE; } }; }