/*
* Copyright (C) 2011 aki@akjava.com
*
* 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 com.akjava.bvh.client;
import java.util.ArrayList;
import java.util.List;
import com.akjava.gwt.lib.client.LineSplitter;
import com.akjava.gwt.lib.client.LineSplitter.SplitterListener;
import com.akjava.gwt.lib.client.LogUtils;
import com.akjava.lib.common.utils.Benchmark;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
public class BVHParser {
private final int EXPECT_HIERARCHY=0;
private final int EXPECT_ROOT=1;
private final int EXPECT_JOINT_OPEN=2;
private final int EXPECT_JOINT_CLOSE=3;
private final int EXPECT_JOINT_INSIDE=4;
private final int EXPECT_ENDSITES_OPEN=5;
private final int EXPECT_ENDSITES_INSIDE=6;
private final int EXPECT_ENDSITES_CLOSE=7;
private final int EXPECT_MOTION=8;
private final int EXPECT_MOTION_FRAMES=9;
private final int EXPECT_MOTION_FRAME_TIME=10;
private final int EXPECT_MOTION_DATA=11;
private int mode;
List<BVHNode> nodes=new ArrayList<BVHNode>();
private BVH bvh;
public static interface ParserListener {
public void onSuccess(BVH bvh);
public void onFaild(String message);
}
//for long text
public void parseAsync(String text,final ParserListener listener){
initialize();
final String replaced=text.replace("\r", "");
Benchmark.start("split");
LineSplitter splitter=new LineSplitter(replaced,10,new SplitterListener() {
@Override
public void onSuccess(List<String> lines) {
//LogUtils.log("splited:"+Benchmark.end("split"));
try {
parseLines(lines.toArray(new String[lines.size()]));
} catch (InvalidLineException e) {
listener.onFaild(e.getMessage());
}
//LogUtils.log("parsed:"+Benchmark.end("parse"));
validate();
listener.onSuccess(bvh);
}
});
Scheduler.get().scheduleEntry(splitter);
}
public BVH parse(String text) throws InvalidLineException{
initialize();
text=text.replace("\r", "");
String lines[]=LineSplitter.splitLineAsArray(text);
parseLines(lines);
validate();
return bvh;
}
public BVH getBvh() {
return bvh;
}
public void parseLines(String[] lines) throws InvalidLineException{
parseLines(lines,0,lines.length);
}
public void parseLines(String[] lines,int start,int end) throws InvalidLineException{
for(int i=start;i<end&&i<lines.length;i++){
String line=lines[i].trim();
if(line.isEmpty()){
continue;//empty always skip
}
switch(mode){
case EXPECT_HIERARCHY:
if(!line.equals("HIERARCHY")){
throw new InvalidLineException(i,line, "Expected HIERARCHY");
}
mode=EXPECT_ROOT;
break;
case EXPECT_ROOT:
if(line.startsWith("ROOT")){
BVHNode node=new BVHNode();
String name=line.substring("ROOT".length()).trim();
node.setName(name);
bvh.setHiearchy(node);
nodes.add(node);
mode=EXPECT_JOINT_OPEN;
}else{
throw new InvalidLineException(i,line, "Expected ROOT");
}
break;
case EXPECT_JOINT_OPEN:
if(line.equals("{")){
mode=EXPECT_JOINT_INSIDE;
}else{
throw new InvalidLineException(i,line, "Expected {");
}
break;
case EXPECT_JOINT_INSIDE:
String[] values=line.split("\\s+");
if(values[0].equals("OFFSET")){
if(values.length!=4){
throw new InvalidLineException(i,line, "OFFSET value need 3 points");
}
double x=toDouble(i,line,values[1]);
double y=toDouble(i,line,values[2]);
double z=toDouble(i,line,values[3]);
getLast().setOffset(new Vec3(x,y,z));
}else if(values[0].equals("CHANNELS")){
if(values.length<2){
throw new InvalidLineException(i,line, "CHANNEL must have 3 values");
}
int channelSize=toInt(i,line,values[1]);
if(channelSize<1){
throw new InvalidLineException(i,line, "CHANNEL size must be larger than 0");
}
if(channelSize!=values.length-2){
throw new InvalidLineException(i,line, " Invalid CHANNEL size:"+channelSize);
}
Channels channel=new Channels();
for(int j=2;j<values.length;j++){
if(values[j].equals("Xposition")){
channel.setXposition(true);
bvh.add(new NameAndChannel(getLast().getName(), Channels.XPOSITION,channel));
}else if(values[j].equals("Yposition")){
channel.setYposition(true);
bvh.add(new NameAndChannel(getLast().getName(), Channels.YPOSITION,channel));
}else if(values[j].equals("Zposition")){
channel.setZposition(true);
bvh.add(new NameAndChannel(getLast().getName(), Channels.ZPOSITION,channel));
}else if(values[j].equals("Xrotation")){
channel.setXrotation(true);
channel.addOrder("X");
bvh.add(new NameAndChannel(getLast().getName(), Channels.XROTATION,channel));
}else if(values[j].equals("Yrotation")){
channel.setYrotation(true);
channel.addOrder("Y");
bvh.add(new NameAndChannel(getLast().getName(), Channels.YROTATION,channel));
}else if(values[j].equals("Zrotation")){
channel.setZrotation(true);
channel.addOrder("Z");
bvh.add(new NameAndChannel(getLast().getName(), Channels.ZROTATION,channel));
}else{
throw new InvalidLineException(i,line, " Invalid CHANNEL value:"+values[j]);
}
}
getLast().setChannels(channel);
}else if(line.equals("End Site")){
mode=EXPECT_ENDSITES_OPEN;
}else if(line.equals("}")){
mode=EXPECT_JOINT_INSIDE;
nodes.remove(getLast());//pop up
if(nodes.size()==0){
mode=EXPECT_MOTION;
}
}else if(values[0].equals("JOINT")){
if(values.length!=2){
throw new InvalidLineException(i,line, " Invalid Joint name:"+line);
}
String name=values[1];
BVHNode node=new BVHNode();
node.setName(name);
getLast().add(node);
nodes.add(node);
mode=EXPECT_JOINT_OPEN;
}else{
throw new InvalidLineException(i,line, " Invalid Joint inside:"+values[0]);
}
break;
case EXPECT_ENDSITES_OPEN:
if(line.equals("{")){
mode=EXPECT_ENDSITES_INSIDE;
}else{
throw new InvalidLineException(i,line, "Expected {");
}
break;
case EXPECT_ENDSITES_INSIDE:
String[] values2=line.split("\\s+");
if(values2[0].equals("OFFSET")){
if(values2.length!=4){
throw new InvalidLineException(i,line, "OFFSET value need 3 points");
}
double x=toDouble(i,line,values2[1]);
double y=toDouble(i,line,values2[2]);
double z=toDouble(i,line,values2[3]);
getLast().addEndSite(new Vec3(x,y,z));
mode=EXPECT_ENDSITES_CLOSE;
}else{
throw new InvalidLineException(i,line, "Endsite only support offset");
}
break;
case EXPECT_ENDSITES_CLOSE:
if(line.equals("}")){
mode=EXPECT_JOINT_CLOSE;
}else{
throw new InvalidLineException(i,line, "Expected {");
}
break;
case EXPECT_JOINT_CLOSE:
if(line.equals("}")){
mode=EXPECT_JOINT_INSIDE;//maybe joint again or close
nodes.remove(getLast());//pop up
if(nodes.size()==0){
mode=EXPECT_MOTION;
}
}else if(line.equals("End Site")){
mode=EXPECT_ENDSITES_OPEN;
}else{
throw new InvalidLineException(i,line, "Expected {");
}
break;
case EXPECT_MOTION:
if(line.equals("MOTION")){
BVHMotion motion=new BVHMotion();
bvh.setMotion(motion);
mode=EXPECT_MOTION_FRAMES;
}else{
throw new InvalidLineException(i,line, "Expected MOTION");
}
break;
case EXPECT_MOTION_FRAMES:
String[] mv=line.split("\\s+");
if(mv[0].equals("Frames:")&& mv.length==2){
int frames=toInt(i, line, mv[1]);
bvh.getMotion().setFrames(frames);
mode=EXPECT_MOTION_FRAME_TIME;
}else{
throw new InvalidLineException(i,line, "Expected Frames:");
}
break;
case EXPECT_MOTION_FRAME_TIME:
String[] mv2=line.split(":");//not space
if(mv2[0].equals("Frame Time")&& mv2.length==2){
double frameTime=toDouble(i, line, mv2[1].trim());
bvh.getMotion().setFrameTime(frameTime);
mode=EXPECT_MOTION_DATA;
}else{
throw new InvalidLineException(i,line, "Expected Frame Time");
}
break;
case EXPECT_MOTION_DATA:
double vs[]=toDouble(i,line);
bvh.getMotion().getMotions().add(vs);
break;
}
}
}
public boolean validate(){
if(bvh.getMotion().getFrames()!=bvh.getMotion().getMotions().size()){
return false;
}
return true;
}
protected static double[] toDouble(String[] values){
double vs[]=new double[values.length];
for(int i=0;i<values.length;i++){
vs[i]=Double.parseDouble(values[i]);
}
return vs;
}
protected double[] toDouble(int index,String line) throws InvalidLineException{
String values[]=line.split("\\s+");
double vs[]=new double[values.length];
try{
for(int i=0;i<values.length;i++){
vs[i]=Double.parseDouble(values[i]);
}
}catch(Exception e){
throw new InvalidLineException(index, line, "has invalid double");
}
return vs;
}
public void initialize() {
bvh=new BVH();
nodes=new ArrayList<BVHNode>();
mode=0;
}
protected BVHNode getLast(){
return nodes.get(nodes.size()-1);
}
protected int toInt(int index,String line,String v) throws InvalidLineException{
int d=0;
try{
d=Integer.parseInt(v);
}catch(Exception e){
throw new InvalidLineException(index,line,"invalid double value:"+v);
}
return d;
}
protected double toDouble(int index,String line,String v) throws InvalidLineException{
double d=0;
try{
d=Double.parseDouble(v);
}catch(Exception e){
throw new InvalidLineException(index,line,"invalid double value:"+v);
}
return d;
}
public class InvalidLineException extends Exception{
/**
*
*/
private static final long serialVersionUID = 1L;
public InvalidLineException(int index,String line,String message){
super("line "+index+":"+line+":message:"+message);
}
}
}