package ru.csu.stan.java.classgen.main;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import ru.csu.stan.java.classgen.automaton.ClassContext;
import ru.csu.stan.java.classgen.automaton.IContext;
import ru.csu.stan.java.classgen.handlers.HandlerFactory;
import ru.csu.stan.java.classgen.handlers.IStaxHandler;
import ru.csu.stan.java.classgen.jaxb.Argument;
import ru.csu.stan.java.classgen.jaxb.Attribute;
import ru.csu.stan.java.classgen.jaxb.BaseTypedElement;
import ru.csu.stan.java.classgen.jaxb.Class;
import ru.csu.stan.java.classgen.jaxb.Classes;
import ru.csu.stan.java.classgen.jaxb.Method;
import ru.csu.stan.java.classgen.jaxb.ObjectFactory;
import ru.csu.stan.java.classgen.jaxb.ParentClass;
import ru.csu.stan.java.classgen.util.ClassIdGenerator;
import ru.csu.stan.java.classgen.util.ClassNameResolver;
/**
* Генератор универсального классового представления.
* Строит представление в 3 прохода:
* <ol>
* <li>1 - Строится основное дерево классов, считывается пакетная структура и структура импортов</li>
* <li>2 - На основе пакетной структуры получаются полные имена классов, системные и библиотечные классы отбрасываются</li>
* <li>3 - Для полных имен генерируются и устанавливаются ID</li>
* </ol>
*
* @author mz
*
*/
public class UCRGenerator {
public ObjectFactory objectFactory;
private HandlerFactory handlersFactory;
private XMLInputFactory xmlFactory;
private UCRGenerator() {
objectFactory = new ObjectFactory();
handlersFactory = HandlerFactory.getInstance();
xmlFactory = XMLInputFactory.newInstance();
}
public static UCRGenerator createInstance() {
return new UCRGenerator();
}
/**
* @param input
* @return
* @throws FactoryConfigurationError
*/
public Classes processInputFile(final String input) throws FactoryConfigurationError {
Classes result = objectFactory.createClasses();
IContext<Classes> context = ClassContext.getInstance(result, objectFactory);
context = firstPass(input, context);
secondPass(result, context);
thirdPass(result);
return result;
}
/**
* Третий проход, расстановка ID
* @param result
*/
private void thirdPass(Classes result) {
System.out.println("Generating IDs for classes");
int id = 0;
for (Class clazz : result.getClazz()){
for (ParentClass parent : clazz.getParent())
parent.setId(ClassIdGenerator.getInstance().getClassId(parent.getName()));
for (Attribute attr: clazz.getAttr()){
generateIdForTypes(attr);
}
id = 0;
for (Method method: clazz.getMethod()){
generateIdForTypes(method);
method.setId(BigInteger.valueOf(++id));
for (Argument arg: method.getArg()){
generateIdForTypes(arg);
}
}
}
}
/**
* @param typedElement
*/
private void generateIdForTypes(BaseTypedElement typedElement) {
if (typedElement.getCommonType().size() > 0)
typedElement.getCommonType().get(0).setId(ClassIdGenerator.getInstance().getClassId(typedElement.getCommonType().get(0).getName()));
if (typedElement.getAggregatedType().size() > 0){
if (typedElement.getAggregatedType().get(0).getId()!= null && typedElement.getAggregatedType().get(0).getId().intValue() != -1)
typedElement.getAggregatedType().get(0).setId(ClassIdGenerator.getInstance().getClassId(typedElement.getAggregatedType().get(0).getName()));
typedElement.getAggregatedType().get(0).setElementId(ClassIdGenerator.getInstance().getClassId(typedElement.getAggregatedType().get(0).getElementType()));
}
}
/**
* Второй проход, установление связей между типами и родительскими классами
*
* @param result
* @param context
*/
private void secondPass(Classes result, IContext<Classes> context) {
System.out.println("Resolving parent classes");
ClassNameResolver nameResolver = ClassNameResolver.getInstance((ClassContext) context);
for (Class clazz : result.getClazz()){
// формируем новый список родителей
List<ParentClass> newParents = new LinkedList<ParentClass>();
for (ParentClass parent : clazz.getParent()){
String fullParentName = nameResolver.getFullTypeName(parent.getName(), clazz, result);
if (fullParentName != null && !fullParentName.isEmpty())
{
parent.setName(fullParentName);
newParents.add(parent);
}
}
// задаем новых отфильтрованых родителей
clazz.getParent().clear();
clazz.getParent().addAll(newParents);
// формируем полные типы для полей
if (clazz.getAttr() != null)
for (Attribute attr: clazz.getAttr()){
nameResolver.resolveTypeNames(attr, clazz, result, objectFactory);
}
// формируем полные типы для возвращаемых значений из методов
if (clazz.getMethod() != null)
for (Method method: clazz.getMethod()){
nameResolver.resolveTypeNames(method, clazz, result, objectFactory);
// формируем полные типы аргументов методов
if (method.getArg() != null)
for (Argument arg: method.getArg()){
nameResolver.resolveTypeNames(arg, clazz, result, objectFactory);
}
}
}
}
/**
* Первый проход обработчика и формирование основного списка классов,
* формирование реестра типов
* @param input
* @param context
* @return
*/
private IContext<Classes> firstPass(final String input, IContext<Classes> context) {
try{
File f = new File(input);
XMLEventReader reader = xmlFactory.createXMLEventReader(new FileInputStream(f));
try{
while (reader.hasNext()){
XMLEvent nextEvent = reader.nextEvent();
IStaxHandler<Classes> handler = handlersFactory.createHandler(nextEvent);
context = handler.handle(context);
}
}
finally{
reader.close();
}
}
catch (FileNotFoundException e) {
System.out.println("File not found!");
e.printStackTrace();
}
catch (XMLStreamException e) {
System.out.println("Wrong XML");
e.printStackTrace();
}
return context;
}
}