/*******************************************************************************
* Copyright (c) 2009 xored software, Inc.
*
* 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:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.tcl.internal.structure;
import org.eclipse.dltk.ast.Modifiers;
import org.eclipse.dltk.compiler.ISourceElementRequestor;
import org.eclipse.dltk.compiler.IElementRequestor.ElementInfo;
import org.eclipse.dltk.compiler.IElementRequestor.TypeInfo;
import org.eclipse.dltk.compiler.util.Util;
import org.eclipse.dltk.tcl.core.TclParseUtil;
import org.eclipse.dltk.tcl.structure.ITclTypeHandler;
import org.eclipse.dltk.tcl.structure.ITclTypeResolver;
import org.eclipse.dltk.tcl.structure.ITclModelBuildContext.ITclModelHandler;
public class TclTypeResolver implements ITclTypeResolver {
private final ISourceElementRequestor fRequestor;
private final TclModelBuildContext fContext;
public TclTypeResolver(ISourceElementRequestor fRequestor,
TclModelBuildContext fContext) {
this.fRequestor = fRequestor;
this.fContext = fContext;
}
private static class ExitFromType implements ITclModelHandler,
ITclTypeHandler {
private int level;
private int end;
private boolean exitFromModule;
private String namespace;
// public boolean created = false;
public ExitFromType(int level, int declEnd, boolean mod, String pop) {
this(level, declEnd, mod, pop, false);
}
public ExitFromType(int level, int declEnd, boolean mod, String pop,
boolean created) {
this.level = level;
this.end = declEnd;
this.exitFromModule = mod;
this.namespace = pop;
// this.created = created;
}
public void leave(ISourceElementRequestor requestor) {
for (int i = 0; i < this.level; i++) {
requestor.exitType(this.end);
}
if (this.exitFromModule) {
requestor.exitModuleRoot();
}
}
public String getNamespace() {
return namespace;
}
}
private static String removeLastSegment(String s) {
if (s.indexOf("::") == -1) {
return Util.EMPTY_STRING;
}
int pos = s.length() - 1;
while (s.charAt(pos) != ':') {
pos--;
}
if (pos > 1) {
return s.substring(0, pos - 1);
} else {
return "::";
}
}
/**
* Enters into required type (if type doesn't exists, creates it). If name
* is fully-qualified (starting with a "::") then it is always resolved
* globally. Else search are done first in current namespace, than in
* global. Flags <code>onlyCurrent</code> allows to search
* <em>not qualified</em> names only in current namespace. If type doesn't
* exists, it will be created. If name is qualified, it will be created
* globally, else in current namespace.s
*
* @param decl
* expression containing typedeclaration correct source ranges
* setup
* @param name
* name containing a type
* @return ExitFromType object, that should be called to exit
*/
public ITclTypeHandler resolveMemberType(ElementInfo decl, int sourceEnd,
String name) {
String type = removeLastSegment(name);
while (type.length() > 2 && type.endsWith("::")) {
type = type.substring(0, type.length() - 2);
}
if (type.length() == 0) {
return new ExitFromType(0, 0, false, null);
}
return resolveTypeImpl(decl, sourceEnd, type);
}
public ITclTypeHandler resolveType(TypeInfo decl, int sourceEnd, String type) {
return resolveTypeImpl(decl, sourceEnd, type);
}
private ITclTypeHandler resolveTypeImpl(ElementInfo decl, int sourceEnd,
String type) {
if (type.equals("::")) {
this.fRequestor.enterModuleRoot();
return new ExitFromType(0, sourceEnd, true, "::");
}
boolean fqn = type.startsWith("::");
String fullyQualified = type;
if (!fqn) { // make name fully-qualified
String e = fContext.getEnclosingNamespace();
if (e == null) {
throw new AssertionError("there are no enclosing namespace!");
}
if (!e.endsWith("::")) {
e += "::";
}
fullyQualified = e + type;
}
// first, try existent
if (this.fRequestor.enterTypeAppend(type, "::")) {
return new ExitFromType(1/* split.length */, sourceEnd, false,
fullyQualified);
}
// create it
// Lets add warning in any case.
int needEnterLeave = 0;
String[] split = null;
String e = fContext.getEnclosingNamespace();
if (e == null) {
throw new AssertionError("there are no enclosing namespace!");
}
boolean entered = false;
boolean exitFromModule = false;
if (e.length() > 0 && !fqn) {
// We need to report warning here.
entered = this.fRequestor.enterTypeAppend(e, "::");
}
if (fqn || !entered) {
split = TclParseUtil.tclSplit(fullyQualified.substring(2));
fRequestor.enterModuleRoot();
exitFromModule = true;
} else {
if (!entered) {
throw new AssertionError("can't enter to enclosing namespace!");
}
needEnterLeave++;
split = TclParseUtil.tclSplit(type);
}
for (int i = 0; i < split.length; ++i) {
if (split[i].length() > 0) {
needEnterLeave++;
if (!fRequestor.enterTypeAppend(split[i], "::")) {
TypeInfo ti = new TypeInfo();
if (decl instanceof TypeInfo) {
ti.modifiers = ((TypeInfo) decl).modifiers;
} else {
ti.modifiers = Modifiers.AccNameSpace;
}
ti.name = split[i];
ti.nameSourceStart = decl.nameSourceStart;
ti.nameSourceEnd = decl.nameSourceEnd;
ti.declarationStart = decl.declarationStart;
if (decl instanceof TypeInfo) {
ti.superclasses = ((TypeInfo) decl).superclasses;
}
this.fRequestor.enterType(ti);
}
}
}
return new ExitFromType(needEnterLeave, sourceEnd, exitFromModule,
fullyQualified, true);
}
}