package archmapper.stylespecific.spring;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;
import archmapper.main.codegenerator.AbstractSourcecodeGenerator;
import archmapper.main.conformance.MappingHelper;
import archmapper.main.exceptions.ArchMapperException;
import archmapper.main.model.architecture.Component;
import archmapper.main.model.architecture.Role.RoleDirection;
import archmapper.main.model.archmapping.ClassDefinition;
import archmapper.main.model.archmapping.ComponentMapping;
import static archmapper.stylespecific.spring.SpringStyleTypeConstants.*;
public class ConfigFileGenerator extends AbstractSourcecodeGenerator {
private final Namespace springBeansNamespace =
Namespace.getNamespace("http://www.springframework.org/schema/beans");
public void generate() {
String filename = archMapping.getProperties().get(CONFIGFILE_PROPERTY);
if (filename == null) {
filename = "applicationContext.xml";
}
IFile configFile = project.getFile("/" + filename);
Document doc = getOrCreateDocument(configFile);
for (Component comp : architecture.getComponents()) {
ComponentMapping compMapping = archMapping.getComponentMapping(comp.getName());
if (compMapping == null) {
continue;
}
if (DAO_COMPONENT_TYPE.equals(comp.getStyleType())) {
handleDAOComponent(compMapping, doc);
} else if (CONTROLLER_COMPONENT_TYPE.equals(comp.getStyleType())) {
handleControllerComponent(comp, compMapping, doc);
}
}
writeConfigFile(doc, configFile);
}
protected void handleDAOComponent(ComponentMapping compMapping, Document doc) {
for (ClassDefinition def : compMapping.getClassDefinition()) {
if (DAO_CLASS_TYPE.equals(def.getType())) {
writeBeanEntry(def.getClassName(), doc, def);
}
}
}
protected void handleControllerComponent(Component comp, ComponentMapping compMapping, Document doc) {
for (ClassDefinition def : compMapping.getClassDefinition()) {
if (CONTROLLER_CLASS_TYPE.equals(def.getType())) {
Element beanElem = writeBeanEntry(def.getClassName(), doc, def);
addBeanReferences(comp, def, beanElem, doc, DAO_COMPONENT_TYPE, DAO_CLASS_TYPE);
for (ClassDefinition valDef : compMapping.getClassesOfType(VALIDATOR_CLASS_TYPE)) {
addProperty(beanElem, doc, "validator", getBeanName(valDef.getClassName()), null);
}
} else if (VALIDATOR_CLASS_TYPE.equals(def.getType())) {
Element beanElem = writeBeanEntry(def.getClassName(), doc, def);
addBeanReferences(comp, def, beanElem, doc, DAO_COMPONENT_TYPE, DAO_CLASS_TYPE);
}
}
}
protected void addBeanReferences(Component comp, ClassDefinition def,
Element beanElem, Document doc, String connectedCompType, String classType) {
for (Component connectedComp : comp.getConnectedComponents(RoleDirection.out)) {
if (connectedCompType.equals(connectedComp.getStyleType())) {
for (ClassDefinition classDef : getClassesOfComponent(connectedComp)) {
if (classType.equals(classDef.getType())) {
addBeanReference(beanElem, getBeanName(classDef.getClassName()), doc);
}
}
}
}
}
protected List<ClassDefinition> getClassesOfComponent(Component comp) {
ComponentMapping compMapping = archMapping.getComponentMapping(comp.getName());
if (compMapping == null) {
return new ArrayList<ClassDefinition>();
}
return compMapping.getClassDefinition();
}
protected Element addBeanReference(Element beanElem, String beanName, Document doc) {
return addProperty(beanElem, doc, beanName, beanName, null);
}
protected Element addProperty(Element beanElem, Document doc, String name, String ref, String value) {
try {
String selectString = "spring:property[@name='"+ name +"']";
XPath xpath = XPath.newInstance(selectString);
xpath.addNamespace("spring", springBeansNamespace.getURI());
Object property = xpath.selectSingleNode(beanElem);
if (property != null) {
return (Element) property;
}
} catch (JDOMException e) {
throw new ArchMapperException("Error searching in the config file: "+ e.getMessage(), e);
}
// If no property was found, create a new one...
Element property = new Element("property", springBeansNamespace);
property.setAttribute("name", name);
if (ref != null) {
property.setAttribute("ref", ref);
}
if (value != null) {
property.setAttribute("name", name);
}
beanElem.addContent(property);
return property;
}
/**
* Returns the bean name from a class name (makes the first character lower case).
*
* @param className
* @return
*/
protected String getBeanName(String className) {
return (""+ className.charAt(0)).toLowerCase() + className.substring(1);
}
protected Element writeBeanEntry(String className, Document doc, ClassDefinition def) {
// make the first character of the bean name lower case
String beanName = getBeanName(className);
String qualifiedClassName = new MappingHelper(model).getQualifiedClassname(def);
Element bean = getBean(qualifiedClassName, doc);
if (bean == null) {
bean = new Element("bean", springBeansNamespace);
bean.setAttribute("id", beanName);
bean.setAttribute("class", qualifiedClassName);
doc.getRootElement().addContent(bean);
}
return bean;
}
protected Element getBean(String className, Document doc) {
try {
String selectString = "//spring:bean[@class='"+ className +"']";
XPath xpath = XPath.newInstance(selectString);
xpath.addNamespace("spring", springBeansNamespace.getURI());
Object beanNode = xpath.selectSingleNode(doc.getRootElement());
return (Element) beanNode;
} catch (JDOMException e) {
throw new ArchMapperException("Error searching in the config file: "+ e.getMessage(), e);
}
}
protected Document getOrCreateDocument(IFile configFile) {
Document doc = new Document();
if (!configFile.exists()) {
ByteArrayInputStream input = new ByteArrayInputStream(new byte[] {});
try {
IPath path = configFile.getParent().getProjectRelativePath();
for (int i=1; i<=path.segmentCount(); i++) {
IFolder folder = project.getFolder(path.uptoSegment(i));
if (!folder.exists()) {
folder.create(true, true, null);
}
}
configFile.create(input, true, null);
} catch (CoreException e) {
throw new ArchMapperException(e);
}
Element root = new Element("beans");
root.setNamespace(springBeansNamespace);
Namespace schemaNS = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
root.addNamespaceDeclaration(schemaNS);
root.setAttribute("schemaLocation", "http://www.springframework.org/schema/beans/spring-beans.xsd", schemaNS);
doc.addContent(root);
} else {
SAXBuilder builder = new SAXBuilder();
try {
doc = builder.build(configFile.getContents(true));
} catch (Exception e) {
throw new ArchMapperException("Error reading existing config file: "+ e.getMessage(), e);
}
}
return doc;
}
protected void writeConfigFile(Document doc, IFile configFile) {
XMLOutputter outp = new XMLOutputter(Format.getPrettyFormat());
FileOutputStream out = null;
try {
out = new FileOutputStream(configFile.getLocation().toOSString());
} catch (FileNotFoundException e) {
throw new ArchMapperException("Could not write to config file: "+ e.getMessage(), e);
}
try {
outp.output(doc, out);
out.close();
} catch (IOException e) {
throw new ArchMapperException("Could not write to config file: "+ e.getMessage(), e);
}
}
}