package com.himamis.retex.editor.share.serializer;
import java.util.HashMap;
import com.himamis.retex.editor.share.model.MathArray;
import com.himamis.retex.editor.share.model.MathCharacter;
import com.himamis.retex.editor.share.model.MathComponent;
import com.himamis.retex.editor.share.model.MathContainer;
import com.himamis.retex.editor.share.model.MathFunction;
import com.himamis.retex.editor.share.model.MathSequence;
import com.himamis.retex.renderer.share.Atom;
import com.himamis.retex.renderer.share.ColorAtom;
import com.himamis.retex.renderer.share.CursorAtom;
import com.himamis.retex.renderer.share.EmptyAtom;
import com.himamis.retex.renderer.share.FencedAtom;
import com.himamis.retex.renderer.share.FractionAtom;
import com.himamis.retex.renderer.share.NthRoot;
import com.himamis.retex.renderer.share.RowAtom;
import com.himamis.retex.renderer.share.ScriptsAtom;
import com.himamis.retex.renderer.share.SelectionAtom;
import com.himamis.retex.renderer.share.SpaceAtom;
import com.himamis.retex.renderer.share.SymbolAtom;
import com.himamis.retex.renderer.share.TeXConstants;
import com.himamis.retex.renderer.share.TeXFormula;
import com.himamis.retex.renderer.share.TeXParser;
import com.himamis.retex.renderer.share.platform.FactoryProvider;
/**
* Directly convert MathComponents into atoms
*
* @author Zbynek
*
*/
public class TeXBuilder {
private MathSequence currentField;
private int currentOffset;
private HashMap<Atom, MathComponent> atomToComponent;
private MathComponent selectionStart;
private MathComponent selectionEnd;
private TeXParser parser;
private Atom buildSequence(MathSequence mathFormula) {
return buildSequence(mathFormula, 0, mathFormula.size() - 1);
}
private Atom buildSequence(MathSequence mathFormula, int from, int to) {
RowAtom ra = new RowAtom(null);
int i= from;
if (mathFormula == currentField && to < from) {
addCursor(ra);
}
while (i <= to) {
if (mathFormula == currentField && i == 0 && currentOffset == 0) {
addCursor(ra);
}
if (mathFormula.getArgument(i + 1) instanceof MathFunction) {
String name = ((MathFunction) mathFormula.getArgument(i + 1))
.getName();
if ("^".equals(name) || "_".equals(name)) {
i++;
continue;
}
}
if (mathFormula.getArgument(i) == selectionStart) {
selectionStart = null;
SelectionAtom sa = new SelectionAtom(
buildSequence(mathFormula, i,
selectionEnd.getParentIndex()),
ColorAtom.getColor("#CCCCFF"),
null);
ra.add(sa);
i = selectionEnd.getParentIndex();
} else {
addArg(ra, mathFormula.getArgument(i));
}
if (mathFormula == currentField && i == currentOffset - 1
&& selectionEnd == null) {
addCursor(ra);
}
i++;
}
if (mathFormula == currentField
&& currentOffset == currentField.size()) {
addCursor(ra);
}
return ra;
}
private void addArg(RowAtom ra, MathComponent argument) {
if (argument instanceof MathCharacter
&& ((MathCharacter) argument).getUnicode() == '=') {
ra.add(space());
ra.add(build(argument));
ra.add(space());
} else {
ra.add(build(argument));
}
}
private static void addCursor(RowAtom ra) {
ra.add(new CursorAtom(FactoryProvider.getInstance().getGraphicsFactory()
.createColor(0, 80, 0), 0.9));
}
private Atom build(MathComponent argument) {
Atom ret = null;
if (argument instanceof MathCharacter) {
ret = newCharAtom(((MathCharacter) argument).getUnicode());
}
else if (argument instanceof MathFunction) {
ret = buildFunction((MathFunction) argument);
}
else if (argument instanceof MathArray) {
ret = buildArray((MathArray) argument);
}
else if (argument instanceof MathSequence) {
ret = buildSequence((MathSequence) argument);
} else {
ret = new EmptyAtom();
}
atomToComponent.put(ret, argument);
return ret;
}
private Atom newCharAtom(char unicode) {
if (parser == null) {
TeXFormula tf = new TeXFormula();
parser = new TeXParser("", tf);
}
Atom ret = parser.convertCharacter(unicode, false);
if (ret instanceof SymbolAtom) {
ret = ret.duplicate();
}
return ret;
}
private Atom space() {
return new SpaceAtom();
}
private Atom buildArray(MathArray argument) {
String leftKey = "lbrack";
if (argument.getOpenKey() == '[') {
leftKey = "lsqbrack";
}
if (argument.getOpenKey() == '{') {
leftKey = "lbrace";
}
String rightKey = "rbrack";
if (argument.getCloseKey() == ']') {
rightKey = "rsqbrack";
}
if (argument.getCloseKey() == '}') {
rightKey = "rbrace";
}
return buildFenced(leftKey, rightKey, argument, 0);
}
private Atom buildFenced(String leftKey, String rightKey,
MathContainer argument, int offset) {
RowAtom row = new RowAtom(null);
for (int i = offset; i < argument.size(); i++) {
if (i > 0) {
row.add(newCharAtom(','));
}
addArg(row, argument.getArgument(i));
}
return new FencedAtom(row,
new SymbolAtom(leftKey, TeXConstants.TYPE_OPENING, true),
new SymbolAtom(rightKey, TeXConstants.TYPE_CLOSING, true));
}
private Atom buildFunction(MathFunction argument) {
if ("^".equals(argument.getName())) {
MathSequence parent = argument.getParent();
int idx = argument.getParentIndex();
return new ScriptsAtom(build(parent.getArgument(idx - 1)), null,
build(argument.getArgument(0)));
}
if ("_".equals(argument.getName())) {
MathSequence parent = argument.getParent();
int idx = argument.getParentIndex();
return new ScriptsAtom(build(parent.getArgument(idx - 1)),
build(argument.getArgument(0)), null);
}
if ("frac".equals(argument.getName())) {
return new FractionAtom(
build(argument.getArgument(0)),
build(argument.getArgument(1)));
}
if ("sqrt".equals(argument.getName())) {
return new NthRoot(build(argument.getArgument(0)), new EmptyAtom());
}
if ("nroot".equals(argument.getName())) {
return new NthRoot(build(argument.getArgument(1)),
build(argument.getArgument(0)));
}
RowAtom row = new RowAtom(null);
for (int i = 0; i < argument.getName().length(); i++) {
row.add(newCharAtom(argument.getName().charAt(i)));
}
row.add(buildFenced("lbrack", "rbrack", argument, 0));
return row;
}
/**
* @param rootComponent
* root
* @param currentField1
* selected field
* @param currentOffset1
* cursor offset within currentField
* @param selectionStart1
* first selected atom
* @param selectionEnd1
* last selected atom
* @return atom representing the whole sequence
*/
public Atom build(MathSequence rootComponent, MathSequence currentField1,
int currentOffset1, MathComponent selectionStart1,
MathComponent selectionEnd1) {
this.currentField = currentField1;
this.currentOffset = currentOffset1;
this.atomToComponent = new HashMap<Atom, MathComponent>();
this.selectionStart = selectionStart1;
this.selectionEnd = selectionEnd1;
return build(rootComponent);
}
/**
* Access the internal mapping atom-> component
*
* @param atom
* atom
* @return corresponding component
*/
public MathComponent getComponent(Atom atom) {
// TODO Auto-generated method stub
return atomToComponent.get(atom);
}
}