/**
* Copyright (c) 2009-2011, The HATS Consortium. All rights reserved.
* This file is licensed under the terms of the Modified BSD License.
*/
package org.absmodels.abs.plugin.editor.outline;
import static org.absmodels.abs.plugin.editor.outline.ABSContentOutlineUtils.*;
import static org.absmodels.abs.plugin.util.Constants.ABS_FILE_EXTENSION;
import static org.absmodels.abs.plugin.util.Constants.EMPTY_OBJECT_ARRAY;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import org.absmodels.abs.plugin.Activator;
import org.absmodels.abs.plugin.builder.AbsNature;
import org.absmodels.abs.plugin.util.InternalASTNode;
import org.absmodels.abs.plugin.util.UtilityFunctions;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
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.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.eclipse.ui.progress.UIJob;
import abs.frontend.ast.*;
import abs.frontend.parser.ABSPackageFile;
/**
* The ABS content outline provider is responsible for dissecting the AST of an
* ABS file (or more specifically a compilation unit) into a tree structure.
* <br/><br/>
* The ABS content provider is set using the TreeViewer's method
* setContentProvider(ITreeContentProvider) during the setup of the TreeViewer.
*
* @author cseise
*
*/
public class ABSContentOutlineProvider implements ITreeContentProvider, IResourceChangeListener, IResourceDeltaVisitor {
private static final ASTNode<?>[] EMPTY_NODES = new ASTNode<?>[0];
private final BaseWorkbenchContentProvider baseProvider = new BaseWorkbenchContentProvider();
private StructuredViewer viewer;
public ABSContentOutlineProvider() {
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
}
/**
* @see
* org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
*/
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof IFile) {
return getChildrenOf((IFile) parentElement);
} else if (parentElement instanceof InternalASTNode<?>) {
InternalASTNode<?> node = (InternalASTNode<?>) parentElement;
synchronized (node.getNature().modelLock) {
ASTNode<?>[] children = getChildrenOfASTNode(node.getASTNode());
return InternalASTNode.wrapASTNodes(children, node.getNature()).toArray();
}
} else if (parentElement instanceof IProject) {
return getChildrenOf((IProject) parentElement);
} else if (parentElement instanceof PackageContainer) {
return ((PackageContainer) parentElement).getPackages().toArray();
} else if (parentElement instanceof PackageEntry) {
return getChildrenOf((PackageEntry) parentElement);
} else if (parentElement instanceof PackageAbsFile) {
return getChildrenOf((PackageAbsFile) parentElement);
}
return EMPTY_OBJECT_ARRAY;
}
private Object[] getChildrenOf(PackageEntry element) {
try {
ABSPackageFile pf = new ABSPackageFile(new File(element.getPath()));
if (pf.isABSPackage()) {
java.util.ArrayList<PackageAbsFile> results = new ArrayList<PackageAbsFile>();
for (Enumeration<JarEntry> e = pf.entries(); e.hasMoreElements();) {
JarEntry jarEntry = e.nextElement();
if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(".abs")) {
results.add(new PackageAbsFile(element, jarEntry.getName()));
}
}
return results.toArray();
}
} catch (IOException e) {
Activator.logException(e);
}
return EMPTY_OBJECT_ARRAY;
}
private Object[] getChildrenOf(IProject project) {
if (project.isOpen()) {
AbsNature nature = UtilityFunctions.getAbsNature(project);
java.util.List<Object> children = new ArrayList<Object>();
children.add(nature.getPackages());
children.addAll(Arrays.asList(baseProvider.getChildren(project)));
return children.toArray();
}
return EMPTY_OBJECT_ARRAY;
}
private ASTNode<?>[] getChildrenOfASTNode(ASTNode<?> parentElement){
if (parentElement instanceof ModuleDecl) {
//no use of static imports here due to name clash with local methods...
return ABSContentOutlineUtils.getChildrenOf((ModuleDecl) parentElement).toArray(EMPTY_NODES);
} else if (parentElement instanceof CompilationUnit) {
return getChildrenOf((CompilationUnit) parentElement);
} else if (parentElement instanceof ClassDecl) {
return getChildrenOf((ClassDecl) parentElement);
} else if (parentElement instanceof InterfaceDecl) {
return getChildrenOf((InterfaceDecl) parentElement);
} else if (parentElement instanceof DataTypeDecl) {
return getChildrenOf((DataTypeDecl) parentElement);
} else if (parentElement instanceof MainBlock) {
return getChildrenOf((MainBlock) parentElement);
} else if (parentElement instanceof List<?>) {
return getChildrenOf((List<?>) parentElement);
} else if (parentElement instanceof DeltaDecl) {
return getChildrenOf((DeltaDecl) parentElement);
}
return EMPTY_NODES;
}
private Object[] getChildrenOf(IFile file) {
if (UtilityFunctions.isABSPackage(file)) {
return getChildren(makeABSPackage(file));
}
return getChildrenOf(new AbsFileImpl(file));
}
private Object[] getChildrenOf(AbsFile file) {
if(!ABS_FILE_EXTENSION.equalsIgnoreCase(file.getFileExtension()))
return null;
AbsNature nature = UtilityFunctions.getAbsNature(file.getProject());
if(nature != null){
CompilationUnit cu = nature.getCompilationUnit(file.getAbsoluteFilePath());
return getChildrenOf(cu,nature);
} else {
return null;
}
}
@SuppressWarnings("unchecked")
private ASTNode<?>[] getChildrenOf(List<?> parentElement) {
List<?> nodeList = (parentElement);
ArrayList<ASTNode<?>> impExp = new ArrayList<ASTNode<?>>();
//Check whether we have an import or an export list
if (isImportList(nodeList)){
for (Import i : (List<Import>)nodeList){
if (isStandardLibImport(i)){
continue;
}
impExp.add(i);
}
return impExp.toArray(EMPTY_NODES);
}else if (isExportList(nodeList)){
for (Export e : (List<Export>)nodeList) {
impExp.add(e);
}
return impExp.toArray(EMPTY_NODES);
}
return EMPTY_NODES;
}
/**
* Retrieve the Children of a CompilationUnit
* @param d The target CompilationUnit
* @return The children of a CompilationUnit, which are relevant for the Content Outline
*/
private InternalASTNode<?>[] getChildrenOf (CompilationUnit cu,AbsNature nature){
if (cu != null){
ArrayList<InternalASTNode<ModuleDecl>> modules = new ArrayList<InternalASTNode<ModuleDecl>>();
for (ModuleDecl d : cu.getModuleDecls()) {
modules.add(new InternalASTNode<ModuleDecl>(d,nature));
}
return modules.toArray(new InternalASTNode<?>[0]);
} else{
return new InternalASTNode<?>[0];
}
}
/**
* Retrieve the Children of a CompilationUnit
* @param d The target CompilationUnit
* @return The children of a CompilationUnit, which are relevant for the Content Outline
*/
private ASTNode<?>[] getChildrenOf (CompilationUnit cu){
if (cu != null){
ArrayList<ASTNode<?>> children = new ArrayList<ASTNode<?>>();
for (ModuleDecl d : cu.getModuleDecls())
children.add(d);
for (DeltaDecl d : cu.getDeltaDecls())
children.add(d);
if (cu.hasProductLine())
children.add(cu.getProductLine());
for (ProductDecl d : cu.getProductDecls())
children.add(d);
for (FeatureDecl d : cu.getFeatureDecls())
children.add(d);
for (FExt d : cu.getFExts())
children.add(d);
return children.toArray(EMPTY_NODES);
} else{
return EMPTY_NODES;
}
}
/**
* Retrieve the Children of a ClassDecl
* @param d The target ClassDecl
* @return The children of a ClassDecl, which are relevant for the Content Outline
*/
private ASTNode<?>[] getChildrenOf(ClassDecl d) {
ArrayList<ASTNode<?>> pDecl = new ArrayList<ASTNode<?>>();
for (FieldDecl fD : d.getFieldList()) {
pDecl.add(fD);
}
for (MethodImpl mD : d.getMethodList()) {
pDecl.add(mD);
}
return pDecl.toArray(EMPTY_NODES);
}
/**
* Retrieve the Children of a DeltaDecl
* @param d The target DeltaDecl
* @return The children of a DeltaDecl, which are relevant for the Content Outline
*/
private ASTNode<?>[] getChildrenOf(DeltaDecl d) {
ArrayList<ASTNode<?>> children = new ArrayList<ASTNode<?>>();
for (DeltaAccess a : d.getDeltaAccessList())
children.add(a);
for (ModuleModifier m : d.getModuleModifiers())
children.add(m);
return children.toArray(EMPTY_NODES);
}
/**
* Retrieve the Children of an InterfaceDecl
* @param d The target InterfaceDecl
* @return The children of an InterfaceDecl, which are relevant for the Content Outline
*/
private ASTNode<?>[] getChildrenOf(InterfaceDecl d) {
ArrayList<ASTNode<?>> pDecl = new ArrayList<ASTNode<?>>();
for (MethodSig mD : d.getBodys()) {
pDecl.add(mD);
}
return pDecl.toArray(EMPTY_NODES);
}
/**
* Retrieve the Children of a MainBlock
* @param d The target MainBlock
* @return The children of a MainBlock, which are relevant for the Content Outline
*/
private ASTNode<?>[] getChildrenOf(MainBlock mb){
ArrayList<ASTNode<?>> pDecl = new ArrayList<ASTNode<?>>();
for (VarDecl mD : mb.getVars()) {
pDecl.add(mD);
}
return pDecl.toArray(EMPTY_NODES);
}
/**
* Retrieve the Children of a DataTypeDecl
* @param d The target DataTypeDecl
* @return The children of a DataTypeDecl, which are relevant for the Content Outline
*/
private ASTNode<?>[] getChildrenOf(DataTypeDecl d){
ArrayList<ASTNode<?>> pDecl = new ArrayList<ASTNode<?>>();
for (DataConstructor dc : d.getDataConstructors()){
pDecl.add(dc);
}
return pDecl.toArray(EMPTY_NODES);
}
private boolean hasChildren(ModuleDecl m){
return (m.getNumDecl() > 0) || (m.getNumImport() > 0) || (m.getNumExport() > 0);
}
private boolean hasChildren(ClassDecl c){
return (c.getNumField() > 0) || (c.getNumMethod() > 0);
}
private boolean hasChildren(DeltaDecl c){
return c.getNumModuleModifier() > 0;
}
@Override
public Object[] getElements(Object inputElement) {
if (inputElement instanceof InternalASTNode){
return getChildren(inputElement);
}else if (inputElement instanceof Model
|| inputElement instanceof CompilationUnit
|| inputElement instanceof ModuleDecl
|| inputElement instanceof ClassDecl
|| inputElement instanceof InterfaceDecl
|| inputElement instanceof DataTypeDecl
|| inputElement instanceof MainBlock
|| inputElement instanceof List<?>
|| inputElement instanceof IFile) {
throw new IllegalArgumentException("No unwrapped ASTNodes should be used. Use InternalASTnodes instead.");
} else if (inputElement instanceof Object[]) {
return (Object[]) inputElement;
}
return EMPTY_OBJECT_ARRAY;
}
@Override
public boolean hasChildren(Object element) {
if (element instanceof InternalASTNode<?>){
InternalASTNode<?> node = ((InternalASTNode<?>) element);
synchronized(node.getNature().modelLock){
return hasChildren(node.getASTNode());
}
} else if (element instanceof IFile) {
return hasChildren((IFile) element);
} else if (element instanceof PackageContainer) {
return ! ((PackageContainer) element).getPackages().isEmpty();
} else if (element instanceof IProject) {
return getChildrenOf((IProject) element).length > 0;
} else if (element instanceof PackageEntry) {
return getChildrenOf((PackageEntry) element).length > 0;
} else if (element instanceof PackageAbsFile) {
Object[] children = getChildrenOf((PackageAbsFile) element);
return children != null && children.length > 0;
}
return false;
}
public boolean hasChildren(ASTNode<?> element) {
if (element instanceof ModuleDecl) {
return hasChildren((ModuleDecl) element);
} else if (element instanceof ClassDecl) {
return hasChildren((ClassDecl) element);
} else if (element instanceof InterfaceDecl) {
return ((InterfaceDecl) element).getNumBody() > 0;
} else if (element instanceof DataTypeDecl) {
return ((DataTypeDecl) element).getNumDataConstructor() > 0;
} else if (element instanceof MainBlock) {
return ((MainBlock) element).getNumVar() > 0;
} else if (element instanceof List<?>) {
return hasChilden((List<?>) element);
} else if (element instanceof DeltaDecl) {
return hasChildren((DeltaDecl) element);
}
return false;
}
/**
* Create a {@link PackageEntry} that represents an {@link PackageAbsFile}
* from an ABS package (as {@link IFile}) in the project.
*
* @param file
* @return
*/
private PackageEntry makeABSPackage(IFile file) {
PackageContainer container = new PackageContainer();
container.setProject(file.getProject());
return new PackageEntry(container,file.getName(),file.getLocation().toString(),false);
}
private boolean hasChildren(IFile file){
if(!UtilityFunctions.isABSFile(file))
return false;
if(UtilityFunctions.isABSPackage(file)) {
return hasChildren(makeABSPackage(file));
}
AbsNature nature = UtilityFunctions.getAbsNature(file);
if(nature != null){
CompilationUnit cu = nature.getCompilationUnit(file);
if(cu != null)
return true;
}
return false;
}
private boolean hasChilden(List<?> element) {
List<?> nodeList = (element);
if (ABSContentOutlineUtils.isImportList(nodeList) || ABSContentOutlineUtils.isExportList(nodeList)){
return true;
} else{
return false;
}
}
@Override
public void dispose() {
baseProvider.dispose();
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
}
@Override
public Object getParent(Object element) {
if (element instanceof InternalASTNode<?>) {
InternalASTNode<?> node = (InternalASTNode<?>) element;
AbsNature nature = node.getNature();
ASTNode<?> parent = node.getASTNode().getParent();
assert nature != null;
/* Root nodes don't have parents, so there's nothing to wrap (ABSTools #301) */
if (parent == null) {
assert node.getASTNode() instanceof Model : element;
return null;
}
return new InternalASTNode<ASTNode<?>>(parent,nature);
}
return null;
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.viewer = (StructuredViewer) viewer;
}
/** Notify viewer that content has changed.
* @author stolz
*/
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
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;
new UIJob("Update CommonViewer") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (viewer != null && !viewer.getControl().isDisposed())
viewer.refresh(file);
return Status.OK_STATUS;
}
}.schedule();
}
return false;
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
IResourceDelta delta = event.getDelta();
try {
delta.accept(this);
} catch (CoreException e) {
Activator.logException(e);
}
}
}