package com.intellij.lang.javascript.flex.importer;
import com.intellij.lang.javascript.JSTokenTypes;
import com.intellij.util.containers.BidirectionalMap;
import com.intellij.util.containers.hash.LinkedHashMap;
import gnu.trove.THashMap;
import java.util.Map;
import java.util.Set;
/**
* @author Maxim.Mossienko
* Date: Oct 20, 2008
* Time: 7:03:18 PM
*/
class Multiname {
private static final String PUBLIC_NS_IN_SOURCE = "public";
private static final String AS3_VEC = "__AS3__.vec";
String[] nsset;
String name;
Multiname(String[] nsset, String name) {
this.nsset = nsset;
this.name = name;
}
public String toString() {
String s = "";
if (hasNotEmptyNs()) s += nsset[0] + "::";
s += name;
return s;
}
boolean hasNotEmptyNs() {
return nsset != null && nsset.length > 0 && nsset[0] != null && nsset[0].length() > 0;
}
public boolean hasNamespace() {
return hasNotEmptyNs() && hasNamespace(nsset[0]);
}
public static boolean hasNamespace(String ns) {
return ns.equals(Abc.PRIVATE_NS) || ns.equals(AS3_VEC) || ns.indexOf('$') != -1 || ns.indexOf(':') != -1;
}
private static final Map<String, String> predefined = new THashMap<>();
static {
predefined.put("http://adobe.com/AS3/2006/builtin", "AS3");
predefined.put("http://www.adobe.com/2006/flex/mx/internal", "mx_internal");
predefined.put(AS3_VEC, "__AS3__$vec");
predefined.put("http://www.adobe.com/2008/actionscript/Flash10/", "flash10");
predefined.put("http://www.adobe.com/2006/actionscript/flash/objectproxy", "object_proxy");
predefined.put("http://www.adobe.com/2006/actionscript/flash/proxy", "flash_proxy");
}
private static final String makeNsIdentifier(String ns, Traits parentTraits) {
String prefix;
String predefinedName = predefined.get(ns);
if (predefinedName != null) prefix = predefinedName;
else {
int i = ns.lastIndexOf('/');
if (i != -1) {
int i2 = ns.lastIndexOf('/', i - 1);
if (i2 != -1) i = i2;
} else {
i = ns.lastIndexOf('.');
if (i != -1) {
int i2 = ns.lastIndexOf('.', i - 1);
if (i2 != -1) i = i2;
}
}
prefix = i != -1 ? makeIdentifier(ns.substring(i + 1)):"ns";
}
int nsCount = 1;
while(true) {
String s = prefix + (nsCount++ == 1 ? "":nsCount);
if (!parentTraits.names.containsKey(s)) {
parentTraits.names.put(ns, null);
return s;
}
}
}
public String getNsName(MemberInfo mi) {
String ns = nsset[0];
if (ns.length() == 0) return PUBLIC_NS_IN_SOURCE;
if (Abc.PRIVATE_NS.equals(ns)) return ns;
if (mi.parentTraits.name == mi.name) return PUBLIC_NS_IN_SOURCE;
Traits parentTraits = mi.parentTraits;
if (ns.equals(parentTraits.protectedNs) ||
(parentTraits.itraits != null && ns.equals(parentTraits.itraits.protectedNs))) {
return "protected";
}
if (parentTraits.name instanceof Multiname) {
Multiname parentName = (Multiname)parentTraits.name;
String parentNs = parentName.nsset[0];
if (parentNs.equals(ns)) {
return "internal";
} else {
int i = ns.indexOf(':');
if (i != -1 &&
ns.regionMatches(0, parentNs, 0, parentNs.length()) &&
ns.regionMatches(parentNs.length() + 1, parentName.name, 0, parentName.name.length()) &&
ns.charAt(parentNs.length()) == ':' &&
ns.length() == parentNs.length() + parentName.name.length() + 1
) {
return "";
} else if (i == -1 && ns.equals(parentName.name) && parentNs.length() == 0) {
return "";
}
}
} else if (parentTraits.name instanceof String) {
String parentName = (String)parentTraits.name;
if (parentName.startsWith(Abc.SCRIPT_PREFIX)) {
if (mi.kind == Abc.TraitType.Const) { // namespace reference
SlotInfo slotInfo = (SlotInfo)mi;
if (slotInfo.type.isStarReference() && slotInfo.value instanceof String) {
return PUBLIC_NS_IN_SOURCE;
}
}
String predefinedNs = predefined.get(ns); // for Vector class def
if (predefinedNs != null) return predefinedNs;
return "public"; // TODO: how to distinguish between public function / constant and not public one
}
if (ns.regionMatches(0, parentName, 0, ns.length())) {
if (ns.length() < parentName.length() && parentName.charAt(ns.length()) == ':') {
return "internal"; // mx.binding::debugDestinationStrings in mx.binding::BindingManager$
}
}
}
if (parentTraits.staticTrait != null) parentTraits = parentTraits.staticTrait;
if (parentTraits.usedNamespacesToNamesMap == null) {
parentTraits.usedNamespacesToNamesMap = new BidirectionalMap<>();
}
String varName = parentTraits.usedNamespacesToNamesMap.get(ns);
if (varName == null) {
varName = makeNsIdentifier(ns, parentTraits);
parentTraits.usedNamespacesToNamesMap.put(ns, varName);
}
return varName;
}
public String getValidNsName(Set<String> classNameTable) {
int nsIndex = 0;
if (nsset.length > 1) {
for (int i = 0; i < nsset.length; i++) {
String ns = nsset[i];
if (!ns.equals(Abc.PUBLIC_NS) && !ns.equals(Abc.PRIVATE_NS) && classNameTable.contains(ns + ":" + name)) {
nsIndex = i;
break;
}
}
}
return nsset[nsIndex];
}
private static String makeIdentifier(String s) {
StringBuilder builder = new StringBuilder(s.length());
for(int i = 0; i < s.length(); ++i) {
char ch = s.charAt(i);
if (!Character.isJavaIdentifierPart(ch)) ch = '_';
builder.append(ch);
}
String s2 = builder.toString();
if (// TODO: we should check !JSTokenTypes.IDENTIFIER_TOKEN_SET.contains(AS3InterfaceDumper.identifierType(s2)) once parser
// can handle nonreserved keywords as namespace reference!
JSTokenTypes.IDENTIFIER != AS3InterfaceDumper.identifierType(s2)) {
s2 = "_" + s2;
}
return s2;
}
public boolean isStarReference() {
return "*".equals(name);
}
}