/*
* xtc - The eXTensible Compiler
* Copyright (C) 2007 Robert Grimm
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package xtc.tree;
import java.util.Collection;
import java.util.ArrayList;
import xtc.util.Pair;
/**
* An annotation capturing source code formatting.
*
* @author Robert Grimm
* @version $Revision: 1.2 $
*/
public abstract class Formatting extends Annotation {
/** A formatting annotation with one node before the annotated node. */
static class Before1 extends Formatting {
Object b1;
Before1(Object b1, Node node) {
super(node);
this.b1 = b1;
}
public int size() {
return 2;
}
public Object get(int index) {
switch (index) {
case 0:
return b1;
case 1:
return node;
default:
throw new IndexOutOfBoundsException("Index: "+index+", Size: 2");
}
}
public Object set(int index, Object value) {
Object old;
switch (index) {
case 0:
old = b1;
b1 = value;
return old;
case 1:
old = node;
node = (Node)value;
return old;
default:
throw new IndexOutOfBoundsException("Index: "+index+", Size: 2");
}
}
}
// =======================================================================
/** A formatting annotation with one node after the annotated node. */
static class After1 extends Formatting {
Object a1;
After1(Node node, Object a1) {
super(node);
this.a1 = a1;
}
public int size() {
return 2;
}
public Object get(int index) {
switch (index) {
case 0:
return node;
case 1:
return a1;
default:
throw new IndexOutOfBoundsException("Index: "+index+", Size: 2");
}
}
public Object set(int index, Object value) {
Object old;
switch (index) {
case 0:
old = node;
node = (Node)value;
return old;
case 1:
old = a1;
a1 = value;
return old;
default:
throw new IndexOutOfBoundsException("Index: "+index+", Size: 2");
}
}
}
// =======================================================================
/**
* A formatting annotation with one node before and after the
* annotated node.
*/
static class Round1 extends Formatting {
Object b1;
Object a1;
Round1(Object b1, Node node, Object a1) {
super(node);
this.b1 = b1;
this.a1 = a1;
}
public int size() {
return 3;
}
public Object get(int index) {
switch (index) {
case 0:
return b1;
case 1:
return node;
case 2:
return a1;
default:
throw new IndexOutOfBoundsException("Index: "+index+", Size: 3");
}
}
public Object set(int index, Object value) {
Object old;
switch (index) {
case 0:
old = b1;
b1 = value;
return old;
case 1:
old = node;
node = (Node)value;
return old;
case 2:
old = a1;
a1 = value;
return old;
default:
throw new IndexOutOfBoundsException("Index: "+index+", Size: 3");
}
}
}
// =======================================================================
/** A generic formatting annotation. */
static class RoundN extends Formatting {
private boolean hasNode;
private ArrayList<Object> before;
private ArrayList<Object> after;
public RoundN() {
hasNode = false;
before = new ArrayList<Object>();
after = new ArrayList<Object>();
}
public boolean hasVariable() {
return true;
}
public void setNode(Node node) {
this.node = node;
hasNode = true;
}
public int size() {
return hasNode ? before.size() + 1 + after.size() : before.size();
}
public Object get(int index) {
if (! hasNode) {
return before.get(index);
} else {
final int size1 = before.size();
final int size2 = after.size();
final int total = size1 + 1 + size2;
if (0 <= index) {
if (index < size1) {
return before.get(index);
} else if (index == size1) {
return node;
} else if (index < total) {
return after.get(index - size1 - 1);
}
}
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+total);
}
}
public Object set(int index, Object value) {
if (! hasNode) {
return before.set(index, value);
} else {
final int size1 = before.size();
final int size2 = after.size();
final int total = size1 + 1 + size2;
if (0 <= index) {
if (index < size1) {
return before.set(index, value);
} else if (index == size1) {
Node old = node;
node = (Node)value;
return old;
} else if (index < total) {
return after.set(index - size1 - 1, value);
}
}
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+total);
}
}
public Node add(Object o) {
if (! hasNode) {
before.add(o);
} else {
after.add(o);
}
return this;
}
public Node addNode(Node node) {
if (hasNode) {
throw new IllegalStateException("Already has annotated node");
}
this.node = node;
hasNode = true;
return this;
}
public Node add(int index, Object o) {
if (! hasNode) {
before.add(index, o);
return this;
} else {
final int size1 = before.size();
final int size2 = after.size();
final int total = size1 + 1 + size2;
if (0 <= index) {
if (index < size1) {
before.add(index, o);
return this;
} else if (index == size1) {
throw new IllegalArgumentException("Can't add to annotated node");
} else if (index < total) {
after.add(index - size1 - 1, o);
return this;
}
}
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+total);
}
}
public Node addAll(Pair<?> p) {
if (! hasNode) {
p.addTo(before);
} else {
p.addTo(after);
}
return this;
}
public Node addAll(Collection<?> c) {
if (! hasNode) {
before.addAll(c);
} else {
after.addAll(c);
}
return this;
}
public Object remove(int index) {
if (! hasNode) {
return before.remove(index);
} else {
final int size1 = before.size();
final int size2 = after.size();
final int total = size1 + 1 + size2;
if (0 <= index) {
if (index < size1) {
return before.remove(index);
} else if (size1 == index) {
throw new IllegalArgumentException("Can't remove annotated node");
} else if (index < total) {
return after.remove(index - size1 - 1);
}
}
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+total);
}
}
}
// =======================================================================
/** Create a new, empty formatting annotation. */
Formatting() { /* Nothing to do. */ }
/**
* Create a new formatting annotation for the specified node.
*
* @param node The node.
*/
Formatting(Node node) {
this.node = node;
}
public boolean hasTraversal() {
return true;
}
// =======================================================================
/**
* Create a formatting annotation. This method returns an
* annotation supporting generic traversal.
*
* @param before The object before the node.
* @param node The annotated node.
* @return The formatting annotation.
*/
public static Formatting before1(Object before, Node node) {
return new Before1(before, node);
}
/**
* Create a formatting annotation. This method returns an
* annotation supporting generic traversal.
*
* @param node The annotated node.
* @param after The object after the node.
* @return The formatting annotation.
*/
public static Formatting after1(Node node, Object after) {
return new After1(node, after);
}
/**
* Create a formatting annotation. This method returns an
* annotation supporting generic traversal.
*
* @param before The object before the node.
* @param node The annotated node.
* @param after The object after the node.
* @return The formatting annotation.
*/
public static Formatting round1(Object before, Node node, Object after) {
return new Round1(before, node, after);
}
/**
* Create a formatting annotation. This method returns an
* annotation supporting generic traversal and adding/removing
* children. All children added before a call to {@link
* #addNode(Node)} or {@link #setNode(Node)} are treated as nodes
* preceding the annotated node, and all children added after such a
* call are treated as nodes succeeding the annotated node.
*
* @return The formatting annotation.
*/
public static Formatting variable() {
return new RoundN();
}
}