/*
* Copyright (C) 2011 Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package ket.math.purpose;
import java.util.*;
import java.awt.Color;
import ket.display.*;
import ket.display.box.Box;
import ket.display.box.BoxTools;
import ket.math.Argument;
import ket.math.Function;
import ket.math.Purpose;
import ket.math.State;
import ket.math.Symbol;
public class CompoundState extends State implements SymbolicState {
public static final int SUBSCRIPT = 0x1;
public static final int JOIN = 0x2;
int displayStyle;
Vector<State> states;
public CompoundState(Vector<Argument> args, int displayStyle) {
states = new Vector<State>();
for (Argument argument : args) {
State state = argument.getState();
states.add(state);
}
// Check
switch (displayStyle) {
case JOIN:
assert size()>1 : "new CompoundState() requires two or more states [args="+args+".";
break;
case SUBSCRIPT:
assert size()==2 :
"new CompoundState() requires two states [args="+
args+"; states="+states+"].";
for (State state : states) {
assert state!=null :
"Cannot create compoundState from null states.\n\t"+
states;
}
break;
default:
throw new RuntimeException("illegal display style");
}
this.displayStyle = displayStyle;
}
public int size() {
return states.size();
}
@Override
public String getFullName() {
switch (displayStyle) {
case JOIN:
String joinedNames = states.firstElement().getFullName().trim();
for (int i=1; i<size(); i++) {
joinedNames += "." + states.get(i).getFullName().trim();
}
return " " + joinedNames + " ";
case SUBSCRIPT:
return states.firstElement().getFullName() + "_" + states.lastElement().getFullName();
default:
throw new RuntimeException("illegal display style");
}
}
@Override
public String getName() {
switch (displayStyle) {
case JOIN:
String joinedNames = states.firstElement().getName().trim();
for (int i=1; i<size(); i++) {
joinedNames += "." + states.get(i).getName().trim();
}
return " " + joinedNames + " ";
case SUBSCRIPT:
assert size()==2;
return states.firstElement().getName() + "_" + states.lastElement().getName();
default:
throw new RuntimeException("illegal display style");
}
}
@Override
public String toUnicode() {
switch (displayStyle) {
case JOIN:
String joinedNames = states.firstElement().toUnicode() + " ";
for (int i=1; i<size(); i++) {
joinedNames += " " + states.get(i).toUnicode();
}
return joinedNames;
case SUBSCRIPT:
assert size()==2;
return states.firstElement().toUnicode() + "_" + states.lastElement().toUnicode();
default:
throw new RuntimeException("illegal display style");
}
}
@Override
public String toLatex() {
switch (displayStyle) {
case JOIN:
String joinedNames = states.firstElement().toLatex() + " ";
for (int i=1; i<size(); i++) {
joinedNames += " " + states.get(i).toLatex();
}
return joinedNames;
case SUBSCRIPT:
assert size()==2;
return "{" + states.firstElement().toLatex() + "_{" + states.lastElement().toLatex() + "}}";
default:
throw new RuntimeException("illegal display style");
}
}
@Override
public String toHTML() {
switch (displayStyle) {
case JOIN:
String joinedNames = states.firstElement().toHTML() + " ";
for (int i=1; i<size(); i++) {
joinedNames += " " + states.get(i).toHTML();
}
return joinedNames;
case SUBSCRIPT:
assert size()==2;
State last = states.lastElement();
//- String lastString = last instanceof Text ? ((Text) last).getHTMLSubscript() : last.toHTML();
String lastString = last.isText() ? ((Text) last).getHTMLSubscript() : last.toHTML();
return states.firstElement().toHTML() + "<SUB>" + lastString + "</SUB>";
default:
throw new RuntimeException("illegal display style");
}
}
public Box toBox(long settings, ColourScheme colourScheme) {
// BUG: Why is this method required?
throw new IllegalStateException("Don't call this method.");
}
@Override
public Box toBox(Argument argument, long settings, ColourScheme colourScheme) {
Vector<Box> boxArgs = new Vector<Box>();
long bold = (settings&Box.BOLD_FONT)!=0 ? Box.BOLD_FONT : 0L;
switch (displayStyle) {
case JOIN:
for (State state : states) {
boxArgs.add(state.toBox(argument, bold, colourScheme));
}
Box box = BoxTools.centredHorizontalBoxList(argument, 0L, boxArgs);
box.setProperty(settings);
return box;
case SUBSCRIPT:
assert size()==2 : "Cannot process subscript size";
Box first = states.firstElement().toBox(
argument,
Box.RIGHT_ALIGN|Box.Y_CENTRE_ALIGN|bold,
colourScheme);
//- first.setArgument(argument);
boxArgs.add(first);
Box subscript = states.lastElement().toBox(
argument,
Box.LEFT_ALIGN|Box.BOTTOM_ALIGN|Box.SMALL_FONT,
colourScheme);
//- subscript.setArgument(argument);
boxArgs.add(subscript);
Box result = Function.SUBSCRIPT.toBox(
argument,
boxArgs,
settings,
colourScheme);
//- result.setArgument(argument);
return result;
default:
throw new RuntimeException("illegal display style");
}
}
@Override
public String toString() {
switch (displayStyle) {
case JOIN:
String joinedNames = "" + states.firstElement();
for (int i=1; i<size(); i++) {
joinedNames += "." + states.get(i);
}
return joinedNames;
case SUBSCRIPT:
assert size()==2;
return states.firstElement() + "_" + states.lastElement();
default:
throw new RuntimeException("illegal display style");
}
}
@Override
public String toVerboseString() {
switch (displayStyle) {
case JOIN:
String joinedNames = states.firstElement().toVerboseString();
for (int i=1; i<size(); i++) {
joinedNames += "." + states.get(i).toVerboseString();
}
return joinedNames;
case SUBSCRIPT:
assert size()==2;
return states.firstElement().toVerboseString() + "_" + states.lastElement().toVerboseString();
default:
throw new RuntimeException("illegal display style");
}
}
@Override
public int iterateToStateForwards(Vector<Argument> args, int index) {
for (int i=index; i<args.size(); i++) {
if (args.get(i)!=null && args.get(i).getState()==this) {
return i;
}
}
return -1;
}
@Override
public int iterateToStateBackwards(Vector<Argument> args) {
for (int i=args.size()-1; i>=0; i--) {
if (args.get(i).getState()==this) {
return i;
}
}
return -1;
}
@Override
public int[] findAllStateIndices(Vector<Argument> args, boolean includeLeft, boolean includeRight) {
Vector<Integer> indices = new Vector<Integer>();
if (includeLeft) {
indices.add(new Integer(-1));
}
int index = this.iterateToStateForwards(args);
while (index!=-1) {
indices.add(new Integer(index));
index = this.iterateToStateForwards(args, index+1);
}
if (includeRight) {
indices.add(new Integer(args.size()));
}
return toIntegerArray(indices);
}
@Override
public boolean matchTypeChar(char type) {
if (type==Purpose.DEFAULT_PREFIX_CHAR) {
return true;
} else if (states.firstElement().toString().charAt(0)==type) {
return true;
} else {
return false;
}
}
@Override
public boolean matches(String pattern) {
return toString().equals(pattern) || getName().equals(pattern);
}
public int getDisplayStyle() {
return displayStyle;
}
public Vector<State> getStates() {
return states;
}
@Override
public boolean equals(Purpose purpose) {
if (purpose instanceof CompoundState) {
// Check that the display styles match and that
// corresponding elements are equal to one another.
CompoundState compoundState = (CompoundState) purpose;
boolean match = true;
match &= this.getDisplayStyle()==compoundState.getDisplayStyle();
Vector<State> thatStates = compoundState.getStates();
match &= (states.size()==thatStates.size());
if (match) {
// Only compare element-wise if their are an
// equal number of states.
for (int i=0; i<states.size(); i++) {
match &= states.get(i).equals(thatStates.get(i));
}
}
return match;
} else {
return false;
}
}
public static final Set<Symbol> MATCHES = new HashSet<Symbol>(
Arrays.asList(
new Symbol[]{
Symbol.RE_ARGUMENT,
Symbol.RE_TOKEN,
Symbol.RE_COMPOUND}));
@Override
public Set<Symbol> getMatchSymbols() {
// This is only updated dynamically in case the arguments are
// replaced.
Set<Symbol> set = new HashSet<Symbol>(MATCHES);
for (State next : states) {
set.addAll(next.getMatchSymbols());
}
return set;
}
@Override
public boolean isVariable() { // Not always true?
return true;
}
}