/*
* Copyright 2013 eXo Platform SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package juzu.impl.template.spi.juzu.ast;
import juzu.impl.common.Coordinate;
import juzu.impl.common.Location;
import juzu.impl.common.MethodInvocation;
import juzu.impl.common.Tools;
import juzu.template.TagHandler;
import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public abstract class ASTNode<N extends ASTNode<N>> implements Serializable {
/** . */
private static final Coordinate DUMB = new Coordinate(0, new Location(1, 1));
/** . */
private final Location beginPosition;
/** . */
protected final List<Block<?>> children;
/** . */
private final List<Block<?>> unmodifiableChildren;
protected ASTNode(Location beginPosition, List<ASTNode.Block<?>> children) {
if (beginPosition == null) {
throw new NullPointerException("No null position accepted");
}
this.beginPosition = beginPosition;
this.children = children;
this.unmodifiableChildren = children != null ? Collections.unmodifiableList(children) : Collections.<Block<?>>emptyList();
}
public Location getBeginPosition() {
return beginPosition;
}
public List<Block<?>> getChildren() {
return unmodifiableChildren;
}
public N addChildren(Iterable<Block<?>> children) {
for (Block child : children) {
addChild(child);
}
return (N)this;
}
public N addChild(Block<?> child) {
if (children == null) {
throw new IllegalStateException("Node " + this + " cannot have children");
}
if (child.parent != null) {
child.parent.children.remove(child);
child.parent = null;
}
child.parent = this;
children.add(child);
return (N)this;
}
public static class Template extends ASTNode<Template> {
public static Template parse(CharSequence s) throws ParseException {
// At this point we could use something like a CharSequenceReader class or something
TemplateParser parser = new TemplateParser(new OffsetTokenManager(new OffsetCharStream(new OffsetReader(new StringReader(s.toString())))));
return parser.parse();
}
public Template() {
super(new Location(0, 0), new ArrayList<Block<?>>());
}
}
public abstract static class Block<B extends Block<B>> extends ASTNode<B> {
/** . */
private final Coordinate begin;
/** . */
private final Coordinate end;
/** . */
private ASTNode<?> parent;
protected Block(Coordinate begin, Coordinate end, List<Block<?>> children) {
super(begin.getPosition(), children);
//
this.begin = begin;
this.end = end;
this.parent = null;
}
public ASTNode<?> getParent() {
return parent;
}
public void addAfter(Block sibling) {
if (sibling.parent != null) {
sibling.parent.children.remove(sibling);
sibling.parent = null;
}
int index = parent.children.indexOf(this);
parent.children.add(index + 1, sibling);
sibling.parent = parent;
}
public void remove() throws IllegalStateException {
if (parent == null) {
throw new IllegalStateException("No parent");
}
parent.children.remove(this);
parent = null;
}
public Coordinate getBegin() {
return begin;
}
public Coordinate getEnd() {
return end;
}
public int getBeginOffset() {
return begin.getOffset();
}
public int getEndOffset() {
return end.getOffset();
}
public Location getEndPosition() {
return end.getPosition();
}
}
public static class Tag extends Block<Tag> {
/** The tag name. */
private final String name;
/** . */
private final Map<String, String> args;
/** . */
private transient TagHandler handler;
public Tag(String name) {
this(name, Collections.<String, String>emptyMap());
}
public Tag(String name, Map<String, String> args) {
this(DUMB, DUMB, name, args);
}
public Tag(Coordinate begin, Coordinate end, String name, Map<String, String> args) {
super(begin, end, new ArrayList<Block<?>>());
//
this.name = name;
this.args = args;
}
public String getName() {
return name;
}
public Map<String, String> getArgs() {
return args;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Tag) {
Tag that = (Tag)obj;
return name.equals(name) && args.equals(that.args) && children.equals(that.children);
}
return false;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[name=" + name + ",args=" + args + "]";
}
}
public static class URL extends Block<URL> {
/** . */
private final String typeName;
/** . */
private final String methodName;
/** . */
private final Map<String, String> args;
/** . */
private MethodInvocation invocation;
public URL(String typeName, String methodName, Map<String, String> args) {
this(DUMB, DUMB, typeName, methodName, args);
}
public URL(Coordinate begin, Coordinate end, String typeName, String methodName, Map<String, String> args) {
super(begin, end, null);
//
this.typeName = typeName;
this.methodName = methodName;
this.args = args;
this.invocation = null;
}
public String getTypeName() {
return typeName;
}
public String getMethodName() {
return methodName;
}
public Map<String, String> getArgs() {
return args;
}
public MethodInvocation getInvocation() {
return invocation;
}
public void setInvocation(MethodInvocation invocation) {
this.invocation = invocation;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof URL) {
URL that = (URL)obj;
return Tools.safeEquals(typeName, that.typeName) && methodName.equals(that.methodName) && args.equals(that.args);
}
return false;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[name=" + methodName + ",args=" + args + "]";
}
}
public static class Section extends Block<Section> {
/** . */
private final SectionType type;
/** . */
private final String text;
public Section(SectionType type, String text) {
this(DUMB, DUMB, type, text);
}
public Section(Coordinate begin, Coordinate end, SectionType type, String text) {
super(begin, end, null);
//
if (type == null) {
throw new NullPointerException();
}
if (text == null) {
throw new NullPointerException();
}
//
this.text = text;
this.type = type;
}
public SectionType getType() {
return type;
}
public String getText() {
return text;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Section) {
Section that = (Section)obj;
return type == that.type && text.equals(that.text);
}
return false;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[type=" + type + ",text=" + text + "]";
}
}
public static class Message extends Block<Message> {
/** . */
private final String key;
public Message(String key) {
this(DUMB, DUMB, key);
}
public Message(Coordinate begin, Coordinate end, String key) {
super(begin, end, null);
//
if (key == null) {
throw new NullPointerException();
}
//
this.key = key;
}
public String getKey() {
return key;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Message) {
Message that = (Message)obj;
return key.equals(that.key);
}
return false;
}
@Override
public String toString() {
return getClass().getSimpleName() + "[key=" + key + "]";
}
}
}