/**
* Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.editor.codecompletion.revisited.javaintegration;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.text.java.CompletionProposalCollector;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.IToken;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_core.structure.Tuple;
/**
* This class defines a module that represents a given java class or package within a java project
* (that's referenced from a jython project).
*
* @author Fabio
*/
public class JavaModuleInProject extends AbstractJavaClassModule {
private static final boolean DEBUG_CLASS_MODULE_IN_PROJECT = false;
private static final int UNKNOWN = -1;
private static final int IS_PACKAGE = 0;
private static final int IS_CLASS = 1;
private IJavaProject javaProject;
private int moduleType = UNKNOWN;
@Override
public IPythonNature getNature() {
return null;
}
/**
* @param name that's the name of the module for jython
* @param javaProject that's the project where it exists.
*/
protected JavaModuleInProject(String name, IJavaProject javaProject) {
super(name);
this.javaProject = javaProject;
if (DEBUG_CLASS_MODULE_IN_PROJECT) {
System.out.println("Created JavaClassModuleInProject: " + name);
}
this.tokens = createTokens(name);
if (DEBUG_CLASS_MODULE_IN_PROJECT) {
System.out.println("JavaClassModuleInProject tokens:");
if (this.tokens == null) {
System.out.println("null tokens");
} else {
for (IToken t : this.tokens) {
System.out.println(t.getRepresentation());
}
}
}
}
/**
* @return whether it's a package or a class.
*/
@Override
public boolean isPackage() {
if (this.moduleType == UNKNOWN) {
throw new RuntimeException("Still can't determine whether it's a package or not.");
}
return this.moduleType == IS_PACKAGE;
}
private File file;
@Override
public File getFile() {
return file;
}
/**
* @see AbstractJavaClassModule#getJavaCompletionProposals(String, String)
*/
@Override
protected List<Tuple<IJavaElement, CompletionProposal>> getJavaCompletionProposals(String completeClassDesc,
String filterCompletionName) throws Exception {
String contents;
if (filterCompletionName != null) {
//pre-filter it a bit if we already know the completion name
contents = "new %s().%s";
contents = StringUtils.format(contents, completeClassDesc, completeClassDesc, filterCompletionName);
} else {
contents = "new %s().";
contents = StringUtils.format(contents, completeClassDesc, completeClassDesc);
}
List<Tuple<IJavaElement, CompletionProposal>> javaCompletionProposals = getJavaCompletionProposals(contents,
contents.length(), filterCompletionName);
if (javaCompletionProposals.size() == 0) {
//Handle static access (notice that we don't create an instance.)
if (filterCompletionName != null) {
//pre-filter it a bit if we already know the completion name
contents = "%s.%s";
contents = StringUtils.format(contents, completeClassDesc, completeClassDesc, filterCompletionName);
} else {
contents = "%s.";
contents = StringUtils.format(contents, completeClassDesc, completeClassDesc);
}
javaCompletionProposals = getJavaCompletionProposals(contents, contents.length() - 2, filterCompletionName);
}
return javaCompletionProposals;
}
/**
* @see AbstractJavaClassModule#getJavaCompletionProposals(String, int, String)
*
* @note: the completionOffset is ignored (we find the type and go for the completions on that type).
*/
@Override
protected List<Tuple<IJavaElement, CompletionProposal>> getJavaCompletionProposals(String contents,
int completionOffset, String filterCompletionName) throws Exception {
try {
IType type = this.javaProject.findType(name);
final List<Tuple<IJavaElement, CompletionProposal>> ret = new ArrayList<Tuple<IJavaElement, CompletionProposal>>();
//we only get actual completions on a class (otherwise, what we have is a package -- which is treated
//as if it was an empty __init__.py file -- without any tokens).
if (type != null) {
getCompletionsForType(contents, filterCompletionName, type, ret);
}
if (this.moduleType == UNKNOWN) {
//if we found the type, it's a class (otherwise it's a package).
if (type == null) {
this.moduleType = IS_PACKAGE;
} else {
this.moduleType = IS_CLASS;
this.file = new File(PydevPlugin.getIResourceOSString(type.getResource()));
}
}
return ret;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
protected IJavaElement findJavaElement(String javaClassModuleName) throws Exception {
return this.javaProject.findType(javaClassModuleName);
}
/**
* Tries to get completions for a given element.
*/
private void getCompletionsForType(String contents, String filterCompletionName, IType type,
final List<Tuple<IJavaElement, CompletionProposal>> ret) throws JavaModelException {
ICompilationUnit unit = type.getCompilationUnit();
if (unit == null) {
return;
}
CompletionProposalCollector collector = createCollector(filterCompletionName, ret, unit);
type.codeComplete(StringUtils.format(contents, name).toCharArray(), -1, 0, new char[0][0], new char[0][0],
new int[0], false, collector);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof JavaModuleInProject)) {
return false;
}
JavaModuleInProject m = (JavaModuleInProject) obj;
if (name == null || m.name == null) {
if (name != m.name) {
return false;
}
//both null at this point
} else if (!name.equals(m.name)) {
return false;
}
if (file == null || m.file == null) {
if (file != m.file) {
return false;
}
//both null at this point
} else if (!file.equals(m.file)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 31;
if (file != null) {
hash += file.hashCode();
}
if (name != null) {
hash += name.hashCode();
}
return hash;
}
}