/*
* $Id: DefinitionStatement.java,v 1.10 2002/09/16 08:05:06 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.script.statements;
import anvil.ErrorListener;
import anvil.Location;
import anvil.java.util.Hashlist;
import anvil.parser.Tag;
import anvil.core.Modules;
import anvil.doc.Doc;
import anvil.codec.ConstantPool;
import anvil.codec.Code;
import anvil.codec.ClassRoom;
import anvil.codec.Field;
import anvil.script.Grammar;
import anvil.script.Name;
import anvil.script.Type;
import anvil.script.Import;
import anvil.script.Imported;
import anvil.script.Scope;
import anvil.script.TypeRef;
import anvil.script.ExternalTypeRef;
import anvil.script.expression.Expression;
import anvil.script.expression.LinkNode;
import anvil.script.parser.TemplateParser;
import anvil.script.compiler.ByteCompiler;
import anvil.script.statements.taglib.TagLibrary;
import anvil.script.statements.taglib.TagLibraryBundle;
import anvil.server.Address;
import anvil.server.Zone;
import anvil.server.ZoneInactiveException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.ArrayList;
import java.net.URL;
/**
* class DefinitionStatement
*
* @author: Jani Lehtim�ki
*/
public abstract class DefinitionStatement extends ScopedStatement implements Scope
{
protected Hashlist _types = new Hashlist();
protected ArrayList _imports = null;
protected HashMap _externals = null;
protected TagLibraryBundle _taglibs = new TagLibraryBundle();
protected DefinitionStatement _parent;
protected String _name;
protected String _descriptor;
protected Doc _document;
protected int _contentstate = CONTENT_COMPRESS;
public DefinitionStatement(DefinitionStatement parent, Location location)
{
super(parent, location);
_parent = parent;
if (parent != null) {
_contentstate = parent.getContentState();
}
}
public DefinitionStatement(
DefinitionStatement parent,
Location location,
String name,
Doc document)
{
super(parent, location);
_parent = parent;
_name = name;
_document = document;
}
public StringBuffer toString(StringBuffer buffer, boolean addDot)
{
if (getType() == Type.MODULE) {
return buffer;
}
_parent.toString(buffer, true);
buffer.append(_name);
if (addDot) {
buffer.append('.');
}
return buffer;
}
public String toString()
{
return toString(new StringBuffer(24)
.append(TYPE_NAMES[getType()])
.append(' '), false)
.toString();
}
public String getQualifiedName()
{
String start = _parent.getQualifiedName();
if (start.length()>0) {
return start + '.' + _name;
} else {
return _name;
}
}
public String getName()
{
return _name;
}
public Statement getParentDefinition()
{
return _parent;
}
public Scope getParent()
{
return _parent;
}
public Doc getDocument()
{
return _document;
}
public int getContentState()
{
return _contentstate;
}
public void setContentState(int state)
{
_contentstate = state;
}
public int getTypeRef(ConstantPool pool)
{
return pool.addClass(_descriptor);
}
public String getDescriptor()
{
return _descriptor;
}
public Enumeration getDeclarations()
{
return _types.elements();
}
public Type lookupDeclaration(String name)
{
if (_externals != null) {
return (Type)_externals.get(name);
}
return null;
}
public boolean isEntityReserved(String name)
{
return false;
}
public int getNextInlined()
{
return 0;
}
public Type lookupLocalDeclaration(String name)
{
return (Type)_types.get(name);
}
public void declare(Type type)
{
_types.put(type.getName(), type);
}
public void declare(String name, Type type)
{
_types.put(name, type);
}
public VariableStatement declare(Location location, String name, Expression expr, String document)
{
return declare(location, name, expr, document, false);
}
public VariableStatement declare(Location location, String name, Expression expr, String document, boolean statik)
{
return null;
}
public void parse(TemplateParser parser, Tag tag)
{
if (tag.contains("pack")) {
_contentstate = CONTENT_PACK;
} else if (tag.contains("compress")) {
_contentstate = CONTENT_COMPRESS;
} else if (tag.contains("preserve")) {
_contentstate = CONTENT_PRESERVE;
} else if (tag.contains("silent")) {
_contentstate = CONTENT_SILENT;
}
}
public void check(ErrorListener context)
{
Enumeration enum = _types.elements();
while(enum.hasMoreElements()) {
Statement stmt = (Statement)enum.nextElement();
stmt.check(context);
}
}
public void compile(ByteCompiler context)
{
Enumeration enum = _types.elements();
while(enum.hasMoreElements()) {
Statement stmt = (Statement)enum.nextElement();
stmt.compile(context);
}
}
protected static void compileMembers(ByteCompiler context, int size, Enumeration types)
{
ClassRoom clazz = context.getClassRoom();
ConstantPool pool = clazz.getPool();
Field members = clazz.createField("_members", "[Ljava/lang/Object;", Code.ACC_PUBLIC|Code.ACC_STATIC);
Code code = clazz.getStatic().getCode();
context.pushCode(code);
int intclazz = pool.addClass("java/lang/Integer");
int intclazzctor = pool.addMethodRef(intclazz, "<init>", "(I)V");
code.iconst(size * 4);
code.anewarray("java/lang/Object");
for(int i=0; types.hasMoreElements();) {
Type type = (Type)types.nextElement();
switch(type.getType()) {
case Type.MODULE:
case Type.CLASS:
case Type.INTERFACE:
case Type.NAMESPACE:
case Type.FUNCTION:
case Type.METHOD:
case Type.INTERFACE_METHOD:
case Type.CONSTRUCTOR:
case Type.CONSTANT_VARIABLE:
case Type.STATIC_VARIABLE:
case Type.MEMBER_VARIABLE:
{
code.dup();
code.iconst(i++);
code.anew(intclazz);
code.dup();
code.iconst(type.getType());
code.invokespecial(intclazzctor);
code.aastore();
code.dup();
code.iconst(i++);
code.astring(type.getName());
code.aastore();
code.dup();
code.iconst(i++);
Doc doc = type.getDocument();
if (doc != null) {
doc.compile(code);
} else {
code.aconst_null();
}
code.aastore();
code.dup();
code.iconst(i++);
switch(type.getType()) {
case Type.FUNCTION:
case Type.METHOD:
case Type.INTERFACE_METHOD:
case Type.CONSTRUCTOR:
((FunctionStatement)type).compileDescriptor(context);
break;
default:
code.aconst_null();
}
code.aastore();
}
break;
default:
break;
}
}
code.putstatic(members);
context.popCode();
}
protected void addExternal(String name, Type type)
{
if (_externals == null) {
_externals = new HashMap();
}
_externals.put(name, new Imported(getModuleStatement(), name, type));
}
protected void addImport(Import imprt)
{
if (_imports == null) {
_imports = new ArrayList();
}
_imports.add(imprt);
getModuleStatement().addDependency(imprt);
}
protected void addExternal(ErrorListener listener, Location location, Object source, String as, Type type)
{
switch(type.getType()) {
case MODULE:
case NAMESPACE:
case CLASS:
case INTERFACE:
case FUNCTION:
case CONSTANT_VARIABLE:
case STATIC_VARIABLE:
case IMPORT:
if (lookupDeclaration(as) == null) {
addExternal(as, type);
} else {
listener.error(location, "Entity '"+type+"' from '"+source+"' causes conflict: name already declared");
}
break;
default:
listener.error(location, "Entity '"+type+"' from '"+source+"' cannot be imported");
break;
}
}
protected static final Type follow(Type type, Name name)
{
int n = name.size();
for(int i=0; i<n; i++) {
if (!(type instanceof Scope)) {
return null;
}
type = ((Scope)type).lookupDeclaration(name.get(i));
}
return type;
}
protected void addExternals(ErrorListener listener, Location location, Object source, Scope scope)
{
Enumeration enum = scope.getDeclarations();
while(enum.hasMoreElements()) {
Type child = (Type)enum.nextElement();
switch(child.getType()) {
case NAMESPACE:
case CLASS:
case INTERFACE:
case FUNCTION:
case CONSTANT_VARIABLE:
case STATIC_VARIABLE:
case IMPORT:
addExternal(listener, location, source, child.getName(), child);
break;
}
}
}
public void importExternals(ErrorListener listener)
{
if (_imports != null) {
int size = _imports.size();
for(int i=0; i<size; i++) {
Import imprt = (Import)_imports.get(i);
Location location = imprt.getLocation();
Object source = imprt.getSource();
Name[] decls = imprt.getDeclarations();
Type type = imprt.resolve(listener);
if (type != null) {
if (imprt.importAll()) {
if (type instanceof Scope) {
addExternals(listener, location, source, (Scope)type);
} else {
listener.error(location, "Trying to import all contained types from non-scoped entity '"+source+"'");
}
} else if (decls != null) {
if (type instanceof Scope) {
int n = decls.length;
for(int j=0; j<n; j++) {
Name childname = decls[j];
Type child = follow(type, childname);
if (child != null) {
if (childname.hasStar()) {
switch(child.getType()) {
case MODULE:
case NAMESPACE:
case CLASS:
case INTERFACE:
addExternals(listener, location, source, (Scope)child);
break;
default:
listener.error(location, "Entity '"+child+"' does not contain any entities that may be imported");
break;
}
} else {
addExternal(listener, location, source, childname.as(), child);
}
} else {
listener.error(location, "Entity '"+childname+"' not found from '"+source+"'");
}
}
} else {
listener.error(location, "Trying to import named entities from non-scoped entity '"+type+"'");
}
} else {
addExternal(listener, location, source, imprt.getAs(), type);
}
}
}
}
Enumeration e = _types.elements();
while(e.hasMoreElements()) {
Statement stmt = (Statement)e.nextElement();
stmt.importExternals(listener);
}
}
public void addEntityImport(
ErrorListener listener,
Location location,
Name entity,
Name[] decls,
boolean withStar)
{
LinkNode link = new LinkNode(this, location, entity);
Import imprt = new Import(null, location, entity, new TypeRef(link), entity.as(), decls, withStar);
addImport(imprt);
}
private static String extractNameFromHref(String href)
{
String name;
int i = href.lastIndexOf('/');
if (i>=0) {
name = href.substring(i+1);
} else {
name = href;
}
i = name.indexOf('.');
if (i>0) {
name = name.substring(0, i);
}
return name;
}
public void addHrefImport(
ErrorListener listener,
Location location,
String href,
String as,
Name[] decls,
boolean withStar)
{
ModuleStatement script = getModuleStatement();
String pathinfo = script.getAddress().merge(href);
Address address = null;
try {
address = script.getAddress().resolve(href);
} catch (ZoneInactiveException e) {
listener.error(location, "Target's zone is inactive: '" + href + "'");
}
if (address.equals(script.getAddress())) {
listener.error(location, "Trying to import self: '" + href + "'");
return;
}
if (as == null) {
as = extractNameFromHref(address.getPathinfo());
}
if (!Grammar.isValidIdentifier(as)) {
listener.error(location, "Identifier '" + as + "' is invalid");
}
Import imprt = new Import(address, location, null,
new ExternalTypeRef(script, address), as, decls, withStar);
addImport(imprt);
}
public void addTaglib(ErrorListener listener, Location location, String source, String namespace, String tagns)
{
ModuleStatement script = getModuleStatement();
Address importing = null;
try {
importing = script.getAddress().resolve(source);
} catch (ZoneInactiveException e) {
listener.error(location, "Target's zone is inactive: '" + source + "'");
}
URL url = importing.getURL();
TagLibrary lib = anvil.script.statements.taglib.Parser.parse(listener, url, location, namespace, tagns);
if (lib != null) {
_taglibs.addLibrary(lib);
}
}
public anvil.script.statements.taglib.Tag getTag(String ns, String name)
{
anvil.script.statements.taglib.Tag tag = _taglibs.getTag(ns, name);
if (tag != null) {
return tag;
}
return super.getTag(ns, name);
}
protected void onImport(TemplateParser parser, Tag tag)
{
String href = tag.getValue("href");
String module = tag.getValue("module");
String from = tag.getValue("from");
String as = tag.getValue("as");
String entities = tag.getValue("entities");
String taglib = tag.getValue("taglib");
String tagns = tag.getValue("ns");
Location location = parser.getLocation();
Name[] decls = null;
boolean star = false;
if (entities != null) {
entities = entities.trim();
if (entities.equals("*")) {
star = true;
} else {
decls = Grammar.parseImportNames(parser, location, entities);
}
}
if (as != null) {
if (star) {
parser.error(location, "Syntax error: 'as' and '*' may not be used together");
}
as = as.trim();
if (!Grammar.isValidIdentifier(as)) {
parser.error(location, "Identifier '" + as + "' is invalid");
as = null;
}
}
if (href != null) {
href = href.trim();
addHrefImport(parser, location, href, as, decls, star);
} else if (from != null) {
Name name = Grammar.parseDottedName(parser, location, from);
if (as != null) {
name.setAs(as);
}
addEntityImport(parser, location, name, decls, star);
} else if (module != null) {
Name name = Grammar.parseDottedName(parser, location, module);
if (as != null) {
name.setAs(as);
}
addEntityImport(parser, location, name, decls, star);
}
if ((taglib != null) && (tagns == null)) {
parser.error(location, "Attribute 'taglib' given but 'ns' is missing");
} else if ((taglib == null) && (tagns != null)) {
parser.error(location, "Attribute 'ns' given but 'taglib' is missing");
} else if ((taglib != null) && (tagns != null)) {
if (star || decls != null) {
parser.error(location, "Import statement declaring tag library must not contain '*'�or 'entities' attribute");
} else {
addTaglib(parser, location, taglib.trim(), as, tagns.trim());
}
}
}
protected void onVar(TemplateParser parser, Tag tag)
{
Location location = parser.getLocation();
String name = tag.getValue("name");
String value = tag.getValue("value");
if (name == null) {
parser.error(location, "Attribute 'name' missing from variable type");
return ;
}
if (!Grammar.isValidIdentifier(name)) {
parser.error(location, "Attribute 'name' is not valid identifier");
return;
}
Expression expr = null;
if (value != null) {
expr = Grammar.parseExpression(value, location, parser);
}
if (tag.contains("static")) {
declare(location, name, expr, parser.getDocument(), true);
} else {
declare(location, name, expr, parser.getDocument());
}
}
protected void onConst(TemplateParser parser, Tag tag)
{
Location location = parser.getLocation();
String name = tag.getValue("name");
String value = tag.getValue("value");
if (name == null) {
parser.error(location, "Attribute 'name' missing from constant type");
return;
}
if (!Grammar.isValidIdentifier(name)) {
parser.error(location, "Attribute 'name' is not valid identifier");
return;
}
if (value == null) {
parser.error(location, "Attribute 'value' missing from constant type");
return;
}
Expression expr = null;
if (value != null) {
expr = Grammar.parseExpression(value, location, parser);
}
declare(new ConstantVariableStatement(location, this, name, expr, parser.getDocument()));
}
protected void onNamespace(TemplateParser parser, int type, Tag tag)
{
Location location = parser.getLocation();
String str = tag.get("name");
if (str == null) {
parser.error(parser.getLocation(), "Attribute 'name' missing from namepace");
NamespaceStatement ns = new NamespaceStatement(location, this);
ns.setName("ns$"+ns.hashCode());
parser.push(ns);
ns.parse(parser, tag);
} else {
NamespaceStatement ns = null;
Name name = Grammar.parseDottedName(parser, location, str);
int n = name.size();
DefinitionStatement scope = this;
for(int i=0; i<n; i++) {
String part = name.get(i);
ns = new NamespaceStatement(location, scope);
ns.setName(part);
Type declared = scope.lookupDeclaration(part);
if (declared == null) {
ns = new NamespaceStatement(location, scope);
ns.setName(part);
scope.declare(ns);
} else if (declared.getType() == Type.NAMESPACE) {
ns = (NamespaceStatement)declared;
} else {
parser.error(location, "Entity '"+name+"' already declared");
ns = new NamespaceStatement(location, scope);
ns.setName(part);
}
parser.push(ns);
ns.parse(parser, tag);
scope = ns;
}
if (ns != null) {
ns.setPopLevel(n);
}
}
}
protected void onFunction(TemplateParser parser, int type, Tag tag)
{
FunctionStatement function = new FunctionStatement(this, parser.getLocation());
function.parse(parser, tag);
parser.push(function);
String name = function.getName();
if (name != null) {
if (lookupDeclaration(name) == null) {
declare(function);
} else {
parser.error(parser.getLocation(), "Entity '" + name + "' is already declared");
}
}
}
protected void onClass(TemplateParser parser, int type, Tag tag)
{
ClassStatement clazz = new ClassStatement(this, parser.getLocation());
parser.push(clazz);
clazz.parse(parser, tag);
String name = clazz.getName();
if (name != null) {
if (lookupDeclaration(name) == null) {
declare(clazz);
} else {
parser.error(parser.getLocation(), "Entity '" + name + "' is already declared");
}
}
}
public boolean onTag(TemplateParser parser, int type, Tag tag)
{
switch(type) {
case ST_IMPORT:
onImport(parser, tag);
return true;
default:
return super.onTag(parser, type, tag);
}
}
}