package nebula.lang;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
import nebula.data.Classificator;
import nebula.data.DataListener;
import nebula.data.InheritSmartList;
import nebula.data.SmartList;
import nebula.data.impl.DataClassificator;
import org.antlr.runtime.ANTLRInputStream;
import org.antlr.runtime.ANTLRReaderStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import util.FileUtil;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
public abstract class TypeLoader {
protected Log log = LogFactory.getLog(this.getClass());
final TypeLoader parent;
final SmartList<String, Type> types;
final Map<String, Type> typesLoading;
protected long lastModified;
final Classificator<String, Type> classifyBy;
Function<Type, String> typeIndexFunction =new Function<Type, String>() {
@Override
public String apply(Type from) {
return from.getName();
}
};
@SuppressWarnings("unchecked")
public TypeLoader(TypeLoader parent) {
this.parent = parent;
if (parent != null) {
types = new InheritSmartList<String, Type>(parent.types, typeIndexFunction);
} else {
types = new SmartList<String, Type>(typeIndexFunction);
}
typesLoading = Maps.newHashMap();
classifyBy = new DataClassificator<String, Type>(new Function<Type, String>() {
@Override
public String apply(Type from) {
return from.getStandalone().toString();
}
});
types.addListener((DataListener<Type>) classifyBy);
}
public void addListener(DataListener<Type> listener) {
types.addListener(listener);
if (this.parent != null) {
parent.addListener(listener);
}
}
//
// public Filter<Type> liveFilter(Predicate<Type> filterFunction) {
// return types.liveFilter(filterFunction);
// }
//
// public List<Type> filter(Predicate<Type> filterFunction) {
// return types.filter(filterFunction);
// }
//
// public <K> Classificator<K, Type> classify(Function<Type, K>
// indexerFunction) {
// return types.classify(indexerFunction);
// }
//
// public <K> Classificator<K, Type> liveClassify(Function<Type, K>
// indexerFunction) {
// return types.liveClassify(indexerFunction);
// }
//TODO refact
public Classificator<String, Type> groupByStandalone() {
return this.classifyBy;
}
List<TypeImp> defineNebula(Reader in) throws RecognitionException {
try {
BufferedReader bin = new BufferedReader(in);
String code = FileUtil.readAllTextFrom(bin);
in = bin;
List<TypeImp> typeList = parse(new ANTLRReaderStream(in));
this.lastModified = System.currentTimeMillis();
for (TypeImp t : typeList) {
t.code = code;
t.url = null;
link(t);
t.lastModified = this.lastModified;
}
types.addAll(typeList);
if (log.isDebugEnabled()) {
log.debug("[ " + typeList.get(0).getName() + " ] loaded succeed");
}
return typeList;
} catch (IOException e) {
throw new NebulaRuntimeException(e);
}
}
List<TypeImp> tryDefineNebula(Reader in) throws RecognitionException, IOException {
BufferedReader bin = new BufferedReader(in);
String code = FileUtil.readAllTextFrom(bin);
in = bin;
List<TypeImp> typeList = parse(new ANTLRReaderStream(in));
this.lastModified = System.currentTimeMillis();
for (TypeImp t : typeList) {
t.code = code;
t.url = null;
t.lastModified = this.lastModified;
}
if (log.isTraceEnabled()) {
log.trace("[ " + typeList.get(0).getName() + " ] loaded succeed from reader ");
}
return typeList;
}
List<TypeImp> defineNebula(URL in) throws IOException, RecognitionException {
String code = FileUtil.readAllTextFrom(in);
InputStream inputStream = null;
URLConnection urlConnection = null;
List<TypeImp> typeList = null;
try {
urlConnection = in.openConnection();
inputStream = urlConnection.getInputStream();
long lastModified = urlConnection.getLastModified();
typeList = parse(new ANTLRInputStream(in.openStream(), "utf-8"));
this.lastModified = lastModified > this.lastModified ? lastModified : this.lastModified;
TypeImp topType = typeList.get(0);
for (TypeImp type : typeList) {
type.code = code;
type.url = in;
type.lastModified = lastModified;
link(type);
topType.subTypes.add(type);
}
types.addAll(typeList);
if (log.isDebugEnabled()) {
log.debug("[ " + typeList.get(0).getName() + " ] loaded succeed from url - " + typeList.get(0).url);
}
} finally {
if (inputStream != null) {
inputStream.close();
}
}
return typeList;
}
protected void link(TypeImp type) {
for (Field f : type.fields) {
f.type.getReferences().add(f);
if(f.attrs.containsKey(Type.ATTACH)){
type.attrs.put(Type.ATTACH_TO, f.type.getName());
f.type.getAttachedBy().add(type);
}
}
}
private List<TypeImp> parse(CharStream in) throws RecognitionException {
NebulaLexer assemblerLexer = new NebulaLexer(in);
CommonTokenStream tokens = new CommonTokenStream(assemblerLexer);
NebulaParser parser = new NebulaParser(tokens, this);
List<TypeImp> typeList = parser.programDefinition();
if (parser.getNumberOfSyntaxErrors() > 0) {
log.error("Parser ERROR!");
throw new NebulaRuntimeException("Parser ERROR!");
}
return typeList;
}
static long cntLevel = 0;
public Type findType(String name) {
if (log.isTraceEnabled()) {
log.trace("\tsearch type [" + name + "] from " + this.getClass().getName());
}
try {
Type type = types.get(name);
if (type != null) {
if (log.isTraceEnabled()) {
log.trace("\tfind type [" + name + "] from " + this.getClass().getName());
}
return type;
}
type = parent.findType(name);
if (type != null) {
if (log.isTraceEnabled()) {
log.trace("\tfind type [" + name + "] from " + this.getClass().getName());
}
return type;
}
if (log.isTraceEnabled()) {
log.trace("[ " + ++cntLevel + " ] " + name + " - try loadClassData within "
+ this.getClass().getName());
}
URL url = loadClassData(name);
if (url != null) {
List<TypeImp> typeList = defineNebula(url);
if (log.isTraceEnabled()) {
log.trace("loaded type [" + name + "] \tfrom " + this.getClass().getName());
}
if (log.isTraceEnabled()) {
log.trace("[ " + cntLevel-- + " ] " + name + " - succeed loadClassData within "
+ this.getClass().getName());
}
return typeList.get(0);
} else {
if (log.isTraceEnabled()) {
log.trace("[ " + cntLevel-- + " ] " + name + " - cannot loadClassData within "
+ this.getClass().getName());
}
return null;
}
} catch (IOException e) {
log.error("IOException ", e);
throw new RuntimeException(e);
} catch (RecognitionException e) {
log.error("RecognitionException ", e);
throw new RuntimeException(e);
}
}
abstract URL loadClassData(String name);
public Type update(Type oldType, String newCode) {
throw new UnsupportedOperationException("change type");
}
public SmartList<String, Type> getList() {
return this.types;
}
public long getLastModified() {
return lastModified;
}
}