/*******************************************************************************
* Copyright © 2008, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.ide.ui.internal.codemanipulation;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.edt.compiler.core.ast.DefaultASTVisitor;
import org.eclipse.edt.compiler.core.ast.File;
import org.eclipse.edt.compiler.core.ast.FunctionInvocation;
import org.eclipse.edt.compiler.core.ast.ImportDeclaration;
import org.eclipse.edt.compiler.core.ast.Name;
import org.eclipse.edt.compiler.core.ast.Node;
import org.eclipse.edt.compiler.core.ast.QualifiedName;
import org.eclipse.edt.compiler.core.ast.SimpleName;
import org.eclipse.edt.ide.core.ast.rewrite.ASTRewrite;
import org.eclipse.edt.ide.core.model.IEGLFile;
import org.eclipse.edt.ide.core.model.IEGLProject;
import org.eclipse.edt.ide.core.model.IPackageFragment;
import org.eclipse.edt.ide.core.model.document.IEGLDocument;
import org.eclipse.edt.ide.ui.EDTUIPlugin;
import org.eclipse.edt.ide.ui.internal.EGLUIStatus;
import org.eclipse.edt.ide.ui.internal.UINlsStrings;
import org.eclipse.edt.ide.ui.internal.editor.util.BoundNodeModelUtility;
import org.eclipse.edt.ide.ui.internal.editor.util.IBoundNodeRequestor;
import org.eclipse.edt.mof.egl.Part;
import org.eclipse.edt.mof.egl.Type;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.ReplaceEdit;
public class AddImportOperation extends OrganizeImportsOperation {
private IEGLDocument fEGLDoc;
private int fSelectionOffset;
private int fSelectionLength;
private IStatus fStatus;
public AddImportOperation(IEGLFile file, IEGLDocument egldoc, int offset, int length, boolean needSave, IChooseImportQuery query) {
super(file, needSave, query);
fEGLDoc = egldoc;
fSelectionOffset = offset;
fSelectionLength = length;
fStatus = Status.OK_STATUS;
}
public IStatus getStatus(){
return fStatus;
}
public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException {
if(monitor == null)
monitor = new NullProgressMonitor();
try{
monitor.beginTask(UINlsStrings.AddImportsOperation_description, 15);
//initial text selection
String identifier = fEGLDoc.get(fSelectionOffset, fSelectionLength);
IPackageFragment currPkgFrag = (IPackageFragment)(feglfile.getParent());
String currPackageName = currPkgFrag.getElementName(); //package name string
final OrganizedImportSection resolvedTypes = new OrganizedImportSection(currPackageName);
Node node = fEGLDoc.getNewModelNodeAtOffset(fSelectionOffset);
Name nameNode = null;
if(node instanceof Name)
{
nameNode = (Name)node;
identifier = nameNode.getIdentifier();
}
if(nameNode != null)
{
final IEGLProject eglProj = feglfile.getEGLProject();
final Set /*<importDeclaration>*/ oldImports = new HashSet();
final int[] oldLastImportEndOffset = new int[1];
File fileAST = fEGLDoc.getNewModelEGLFile();
fileAST.accept(new DefaultASTVisitor() {
public boolean visit(File file) {return true;};
public boolean visit(ImportDeclaration importDeclaration) {
oldImports.add(importDeclaration);
Name importName = importDeclaration.getName();
String packageName = ""; //$NON-NLS-1$
if(!importDeclaration.isOnDemand())
{
String existingPartName = importName.getIdentifier();
if(importName instanceof QualifiedName)
packageName = ((QualifiedName)importName).getQualifier().getCanonicalName();
resolvedTypes.addImport(packageName, existingPartName); //even if the part name could be duplicate, the data structure is a map, it will ignore the duplicate part name
}
else //if onDemand
{
packageName = importName.getCanonicalName();
resolvedTypes.addImport(packageName, "*"); //$NON-NLS-1$
}
return false;
}
public void endVisit(ImportDeclaration importDeclaration) {
oldLastImportEndOffset[0] = importDeclaration.getOffset() + importDeclaration.getLength();
}
});
ASTRewrite rewrite = ASTRewrite.create(fileAST);
//need to add to import section
Map /*<String>, <Name>*/ unresolvedTypes = new LinkedHashMap();
unresolvedTypes.put(identifier, nameNode);
try2ResolveUnresolvedTypes(resolvedTypes, unresolvedTypes, eglProj, oldImports, new SubProgressMonitor(monitor, 5));
//update the file
writeToFile(resolvedTypes, oldImports, fEGLDoc, rewrite, fileAST, new SubProgressMonitor(monitor, 1));
final int[] newLastImportEndOffset = new int[1];
fileAST = fEGLDoc.getNewModelEGLFile();
fileAST.accept(new DefaultASTVisitor() {
public boolean visit(File file) {return true;};
public void endVisit(ImportDeclaration importDeclaration) {
newLastImportEndOffset[0] = importDeclaration.getOffset() + importDeclaration.getLength();
}
});
int offsetdelta = newLastImportEndOffset[0]-oldLastImportEndOffset[0];
//need to get bound ast to get the type binding
//if it is part binding, then reduce to the simple name from qualified name if it doesn't conflict with the exsiting imports.
IFile file = (IFile)(feglfile.getCorrespondingResource());
BoundNodeModelUtility.getBoundNodeAtOffset(file, fSelectionOffset+offsetdelta, new IBoundNodeRequestor(){
public void acceptNode(Node boundPart, Node selectedNode) {
if(selectedNode instanceof QualifiedName){
QualifiedName qualifiednamenode = (QualifiedName)selectedNode;
String fullyQualifiedPackageName = qualifiednamenode.getQualifier().getCanonicalName();
String[] conflictPkgNames = new String[1];
String qualifiedNameID = qualifiednamenode.getIdentifier();
if(!resolvedTypes.isConflict(fullyQualifiedPackageName, qualifiedNameID, conflictPkgNames))
{
Type typeBinding = qualifiednamenode.resolveType();
if(typeBinding instanceof Part){
String names = qualifiednamenode.getNameComponents();
int lastDot = names.lastIndexOf('.');
ReplaceEdit replace = new ReplaceEdit(selectedNode.getOffset(), selectedNode.getLength(), lastDot == -1 ? names : names.substring(lastDot + 1));
try {
replace.apply(fEGLDoc);
} catch (MalformedTreeException e) {
EDTUIPlugin.log(e);
} catch (BadLocationException e) {
EDTUIPlugin.log(e);
}
}
}
else
{
String conflictImportEntry = conflictPkgNames[0];
if(conflictImportEntry.length()>0)
conflictImportEntry += "."; //$NON-NLS-1$
conflictImportEntry += qualifiedNameID;
fStatus= EGLUIStatus.createError(IStatus.ERROR, MessageFormat.format(UINlsStrings.AddImportsOperation_error_importclash, new String[]{conflictImportEntry}), null);
}
}
}
});
}
else //if(nameNode == null)
{
//selection is not a name node
fStatus = EGLUIStatus.createError(IStatus.ERROR, MessageFormat.format(UINlsStrings.AddImportsOperation_error_notresolved_message, new String[]{identifier}), null);
}
} catch (BadLocationException e) {
throw new CoreException(EGLUIStatus.createError(IStatus.ERROR, e));
}finally {
monitor.done();
}
}
/**
* get the fully qualified name's package name if possible
* if the input nameNode is simple name, it could be "pkg3" in pkg3.pkg4.partA
* we want to return the fully qualified name "partA" in pkg3.pkg4.partA
*
* @param nameNode
* @param qualifiedPkgNameString
* @return
*/
private QualifiedName getFullyQualifedNameNodeIfPossible(Name nameNode)
{
final QualifiedName[] possibleQualifiedName = new QualifiedName[]{null}; //init the 1st element to be null
if(nameNode instanceof QualifiedName)
{
possibleQualifiedName[0] = (QualifiedName)nameNode;
}
if(nameNode instanceof SimpleName)
{
SimpleName simpleName = (SimpleName)nameNode;
simpleName.getParent().accept(new DefaultASTVisitor(){
public boolean visit(QualifiedName qualifiedName) {
qualifiedName.getParent().accept(this);
return false;
}
public void endVisit(QualifiedName qualifiedName) {
if(possibleQualifiedName[0] == null) { //only want the 1st one
possibleQualifiedName[0] = qualifiedName;
if(qualifiedName.getParent() instanceof FunctionInvocation && qualifiedName == ((FunctionInvocation) qualifiedName.getParent()).getTarget()) {
Name possibleQualifier = ((QualifiedName) qualifiedName).getQualifier();
if(possibleQualifier.isQualifiedName()) {
possibleQualifiedName[0] = (QualifiedName) possibleQualifier;
}
else {
possibleQualifiedName[0] = null;
}
}
}
};
});
}
return possibleQualifiedName[0];
}
/* private boolean foundPart(IEGLProject eglProj, String packageName, String partName, IProgressMonitor monitor) throws CoreException
{
IEGLSearchScope projScope = SearchEngine.createEGLSearchScope(new IEGLElement[]{eglProj}, true);
List typeList = new ArrayList();
try {
new SearchEngine().searchAllPartNames(ResourcesPlugin.getWorkspace(),
packageName.toCharArray(),
partName.toCharArray(),
IEGLSearchConstants.EXACT_MATCH,
IEGLSearchConstants.CASE_INSENSITIVE,
IEGLSearchConstants.PART,
projScope,
new PartInfoRequestor(typeList),
IEGLSearchConstants.WAIT_UNTIL_READY_TO_SEARCH,
monitor);
} catch (EGLModelException e) {
throw new CoreException(EGLUIStatus.createError(IStatus.ERROR, e));
}
int foundCnts = typeList.size();
if(foundCnts == 1)
return true;
else
return false; //if more than 1 found, something is wrong
}
*/
}