package org.eclipse.dltk.tcl.internal.core.search.mixin;
import java.util.List;
import java.util.Stack;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.FieldDeclaration;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.mixin.IMixinRequestor;
import org.eclipse.dltk.core.mixin.IMixinRequestor.ElementInfo;
import org.eclipse.dltk.tcl.ast.TclStatement;
import org.eclipse.dltk.tcl.core.TclParseUtil;
import org.eclipse.dltk.tcl.core.ast.TclPackageDeclaration;
import org.eclipse.dltk.tcl.core.extensions.IMixinBuildVisitorExtension;
import org.eclipse.dltk.tcl.internal.core.TclExtensionManager;
import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclField;
import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclNamespace;
import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclNamespaceImport;
import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclPackage;
import org.eclipse.dltk.tcl.internal.core.search.mixin.model.TclProc;
public class TclMixinBuildVisitor extends ASTVisitor {
protected boolean signature = false;
protected ISourceModule sourceModule;
protected ModuleDeclaration moduleDeclaration;
protected IMixinRequestor requestor;
protected Stack<TypeDeclaration> namespaceNames = new Stack<TypeDeclaration>();
protected IMixinBuildVisitorExtension[] extensions;
public TclMixinBuildVisitor(ModuleDeclaration moduleDeclaration,
ISourceModule module, boolean signature, IMixinRequestor requestor) {
this.signature = signature;
this.sourceModule = module;
this.moduleDeclaration = moduleDeclaration;
this.requestor = requestor;
this.extensions = TclExtensionManager.getDefault()
.getMixinVisitorExtensions();
}
public boolean endvisit(TypeDeclaration s) throws Exception {
// Skip type names with code execution
if (s.getName().indexOf('[') != -1 || s.getName().indexOf('$') != -1) {
return true;
}
this.namespaceNames.pop();
return true;
}
public String getKeyFromLevels(List nodes) {
return TclParseUtil.getElementFQN(nodes,
IMixinRequestor.MIXIN_NAME_SEPARATOR, this.moduleDeclaration);
}
public String getNamespacePrefix() {
StringBuffer prefix = new StringBuffer();
for (TypeDeclaration ns : this.namespaceNames) {
String name = ns.getName();
if (name.endsWith("::")) {
name = name.substring(0, name.length() - 2);
}
if (name.startsWith("::")) {
// Remove all previous
prefix.setLength(0);
name = name.substring(2);
}
prefix.append(tclNameToKey(name)
+ IMixinRequestor.MIXIN_NAME_SEPARATOR);
}
return prefix.toString();
}
public String getRealNamespacePrefix() {
StringBuffer prefix = new StringBuffer();
for (TypeDeclaration ns : this.namespaceNames) {
String name = ns.getName();
if (name.endsWith("::")) {
name = name.substring(0, name.length() - 2);
}
if (name.startsWith("::")) {
// Remove all previous
prefix.setLength(0);
name = name.substring(2);
}
prefix.append(name + "::");
}
String result = prefix.toString();
if (result.startsWith("::")) {
result = result.substring(2);
}
if (result.endsWith("::")) {
result = result.substring(0, result.length() - 2);
}
return result;
}
public String tclNameToKey(String name) {
return TclParseUtil.tclNameTo(name,
IMixinRequestor.MIXIN_NAME_SEPARATOR);
}
public boolean visit(MethodDeclaration s) throws Exception {
for (int i = 0; i < this.extensions.length; i++) {
if (this.extensions[i].visit(s, this)) {
return true;
}
}
// Other methods visit
ElementInfo info = new ElementInfo();
String name = s.getName();
if (name.startsWith("::")) {
info.key = tclNameToKey(name.substring(2));
} else {
if (name.indexOf("::") == -1) {
info.key = this.getNamespacePrefix() + tclNameToKey(name);
} else {
TypeDeclaration type = TclParseUtil.findTclTypeDeclarationFrom(
this.moduleDeclaration, s);
if (type != null) {
List levels = TclParseUtil.findLevelsTo(
this.moduleDeclaration, type);
String[] mName = TclParseUtil.tclSplit(name);
info.key = this.getKeyFromLevels(levels)
+ IMixinRequestor.MIXIN_NAME_SEPARATOR
+ mName[mName.length - 1];
} else { // case of addint to unknown namespace.
info.key = tclNameToKey(name);
}
}
}
if (info.key != null) {
if (signature) {
info.object = new TclProc();
}
this.requestor.reportElement(info);
}
// System.out.println("Report proc or instproc:" + info.key);
return true;
}
public boolean visit(Statement s) throws Exception {
for (int i = 0; i < this.extensions.length; i++) {
if (this.extensions[i].visit(s, this)) {
return false;
}
}
processPackageStatements(s);
if (s instanceof TclStatement) {
processNamespaceImport(s);
}
if (s instanceof FieldDeclaration) {
FieldDeclaration field = (FieldDeclaration) s;
ElementInfo info = new ElementInfo();
// We need to filter arrays.
if (!TclParseUtil.isArrayVariable(field.getName())) {
List levels = TclParseUtil.findLevelsTo(this.moduleDeclaration,
s);
info.key = TclParseUtil
.getElementFQN(levels,
IMixinRequestor.MIXIN_NAME_SEPARATOR,
moduleDeclaration)// this.getNamespacePrefix()
+ IMixinRequestor.MIXIN_NAME_SEPARATOR
+ this.tclNameToKey(field.getName());
if (info.key.startsWith("{")) {
info.key = info.key.substring(1);
}
} else {
info.key = this.getNamespacePrefix()
// + IMixinRequestor.MIXIN_NAME_SEPARATOR
+ this.tclNameToKey(TclParseUtil.extractArrayName(field
.getName()));
}
if (signature) {
info.object = new TclField();
}
this.requestor.reportElement(info);
}
return super.visit(s);
}
static boolean isValidPackageName(String packageName) {
return packageName != null && packageName.length() != 0
&& packageName.indexOf('$') == -1
&& packageName.indexOf('[') == -1
&& packageName.indexOf(']') == -1;
}
private void processPackageStatements(Statement s) {
if (s instanceof TclPackageDeclaration) {
final TclPackageDeclaration pkg = (TclPackageDeclaration) s;
if (pkg.getStyle() == TclPackageDeclaration.STYLE_IFNEEDED
|| pkg.getStyle() == TclPackageDeclaration.STYLE_PROVIDE) {
if (isValidPackageName(pkg.getName())) {
ElementInfo info = new ElementInfo();
String version = "";
if (pkg.getVersion() != null
&& pkg.getVersion() instanceof SimpleReference) {
version = ((SimpleReference) pkg.getVersion())
.getName();
}
info.key = TclPackage.makeKey(pkg.getName(), version,
TclPackage.PROVIDE);
if (info.key != null) {
if (signature) {
info.object = new TclPackage(pkg.getName(),
version, TclPackage.PROVIDE);
}
this.requestor.reportElement(info);
}
}
}
}
}
private void processNamespaceImport(Statement s) {
TclStatement st = (TclStatement) s;
if (st.getCount() > 2) {
String cmdValue = getValue(st.getAt(0));
if (!"namespace".equals(cmdValue)) {
return;
}
String arg0 = getValue(st.getAt(1));
if (!"import".equals(arg0)) {
return;
}
String currentNamespace = getRealNamespacePrefix();
for (int i = 2; i < st.getCount(); i++) {
String value = getValue(st.getAt(i));
if (value != null && !"-force".equals(value)) {
ElementInfo info = new ElementInfo();
value = TclNamespaceImport.processPattern(value);
info.key = TclNamespaceImport.makeKey(currentNamespace,
value);
if (info.key != null) {
if (signature) {
info.object = new TclNamespaceImport(
currentNamespace, value);
}
this.requestor.reportElement(info);
}
}
}
}
}
private String getValue(Expression cmd) {
if (cmd instanceof SimpleReference) {
String cmdValue = ((SimpleReference) cmd).getName();
if (cmdValue.startsWith("::")) {
cmdValue = cmdValue.substring(2);
}
return cmdValue;
}
return null;
}
public boolean visit(TypeDeclaration s) throws Exception {
// Skip type names with code execution
if (s.getName().indexOf('[') != -1 || s.getName().indexOf('$') != -1) {
return true;
}
for (int i = 0; i < this.extensions.length; i++) {
if (extensions[i].visit(s, this)) {
return true;
}
}
// This is Tcl namespaces
ElementInfo info = new ElementInfo();
info.key = this.getNamespacePrefix() + tclNameToKey(s.getName());
if (info.key.startsWith("{")) {
info.key = info.key.substring(1);
}
// System.out.println("Report XOTcl type:" + info.key);
this.namespaceNames.push(s);
if (signature) {
info.object = new TclNamespace();
}
this.requestor.reportElement(info);
return true;
}
public boolean getSignature() {
return this.signature;
}
public IMixinRequestor getRequestor() {
return this.requestor;
}
public ModuleDeclaration getModuleDeclaration() {
return moduleDeclaration;
}
public void pushNamespaceName(TypeDeclaration s) {
this.namespaceNames.push(s);
}
}