0001: /*
0002: * Copyright 2005-2007 The Kuali Foundation.
0003: *
0004: *
0005: * Licensed under the Educational Community License, Version 1.0 (the "License");
0006: * you may not use this file except in compliance with the License.
0007: * You may obtain a copy of the License at
0008: *
0009: * http://www.opensource.org/licenses/ecl1.php
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.kuali.workflow.tools.xml;
0018:
0019: import java.io.BufferedInputStream;
0020: import java.io.File;
0021: import java.io.FileInputStream;
0022: import java.io.FileNotFoundException;
0023: import java.io.FileOutputStream;
0024: import java.io.FilenameFilter;
0025: import java.io.InputStream;
0026: import java.io.StringWriter;
0027: import java.util.ArrayList;
0028: import java.util.Arrays;
0029: import java.util.Collections;
0030: import java.util.Comparator;
0031: import java.util.HashMap;
0032: import java.util.HashSet;
0033: import java.util.Iterator;
0034: import java.util.List;
0035: import java.util.Map;
0036: import java.util.Set;
0037:
0038: import javax.xml.parsers.DocumentBuilderFactory;
0039: import javax.xml.transform.OutputKeys;
0040: import javax.xml.transform.Result;
0041: import javax.xml.transform.Source;
0042: import javax.xml.transform.Transformer;
0043: import javax.xml.transform.TransformerFactory;
0044: import javax.xml.transform.dom.DOMSource;
0045: import javax.xml.transform.stream.StreamResult;
0046: import javax.xml.xpath.XPath;
0047: import javax.xml.xpath.XPathConstants;
0048: import javax.xml.xpath.XPathFactory;
0049:
0050: import org.w3c.dom.Document;
0051: import org.w3c.dom.Element;
0052: import org.w3c.dom.Node;
0053: import org.w3c.dom.NodeList;
0054: import org.xml.sax.InputSource;
0055:
0056: /**
0057: * A utility class to generate Workgroup and Rule XML from an Excel spreadsheet.
0058: *
0059: * @author ewestfal
0060: */
0061: public class XmlGen {
0062:
0063: // arguments
0064: private String outputDirectoryPath;
0065: private String inputDirectoryPath;
0066: private XmlGenHelper xmlGenHelper;
0067:
0068: private XPath xpath;
0069: private Map<String, Attribute> attributes = new HashMap<String, Attribute>();
0070: private Map<String, Template> templates = new HashMap<String, Template>();
0071: private Map<String, DocumentType> documentTypes = new HashMap<String, DocumentType>();
0072: private SpreadsheetOutput output = new SpreadsheetOutput();
0073:
0074: private static final String IGNORE_PREVIOUS = "IGNORE_PREVIOUS";
0075: private static final String ACTION_REQUESTED = "ACTION_REQUESTED";
0076: private static final String DESCRIPTION = "DESCRIPTION";
0077: private static final String COMMENT = "COMMENT";
0078:
0079: public static void main(String[] args) throws Exception {
0080: String helperClassName = null;
0081: String outputDir = null;
0082: String inputDir = null;
0083: for (int index = 0; index < args.length; index++) {
0084: String arg = args[index];
0085: if (arg.equals("-helper")) {
0086: if (index + 1 >= args.length) {
0087: fail("Failed to parse arguments.");
0088: }
0089: helperClassName = args[++index];
0090: } else if (arg.equals("-o")) {
0091: if (index + 1 >= args.length) {
0092: fail("Failed to parse arguments.");
0093: }
0094: outputDir = args[++index];
0095: } else if (arg.equals("-h")) {
0096: usage();
0097: System.exit(0);
0098: } else if (index == (args.length - 1)) {
0099: inputDir = args[index];
0100: }
0101: }
0102: if (isBlank(inputDir)) {
0103: fail("No input directory was specified.");
0104: }
0105: XmlGenHelper helper = null;
0106: if (!isBlank(helperClassName)) {
0107: try {
0108: Class helperClass = Class.forName(helperClassName);
0109: Object newHelperInstance = helperClass.newInstance();
0110: if (!(newHelperInstance instanceof XmlGenHelper)) {
0111: fail("Given helper class '" + helperClassName
0112: + "' is not a valid instance of '"
0113: + XmlGenHelper.class.getName() + "'");
0114: } else {
0115: helper = (XmlGenHelper) newHelperInstance;
0116: }
0117: } catch (ClassNotFoundException e) {
0118: fail("Given helper class '"
0119: + helperClassName
0120: + "' could not be found. Please ensure that it is available on the classpath.");
0121: }
0122: }
0123: XmlGen xmlGen = new XmlGen();
0124: xmlGen.setInputDirectoryPath(inputDir);
0125: xmlGen.setOutputDirectoryPath(outputDir);
0126: xmlGen.setXmlGenHelper(helper);
0127: xmlGen.run();
0128: }
0129:
0130: private static void fail(String errorMessage) {
0131: System.err.println("\n>>>>> " + errorMessage);
0132: usage();
0133: System.exit(-1);
0134: }
0135:
0136: public Map<String, Attribute> getAttributes() {
0137: return attributes;
0138: }
0139:
0140: public Map<String, DocumentType> getDocumentTypes() {
0141: return documentTypes;
0142: }
0143:
0144: public Map<String, Template> getTemplates() {
0145: return templates;
0146: }
0147:
0148: public String getInputDirectoryPath() {
0149: return inputDirectoryPath;
0150: }
0151:
0152: public void setInputDirectoryPath(String inputDirectory) {
0153: this .inputDirectoryPath = inputDirectory;
0154: }
0155:
0156: public String getOutputDirectoryPath() {
0157: return outputDirectoryPath;
0158: }
0159:
0160: public void setOutputDirectoryPath(String outputDirectory) {
0161: this .outputDirectoryPath = outputDirectory;
0162: }
0163:
0164: public XmlGenHelper getXmlGenHelper() {
0165: return xmlGenHelper;
0166: }
0167:
0168: public void setXmlGenHelper(XmlGenHelper xmlGenHelper) {
0169: this .xmlGenHelper = xmlGenHelper;
0170: }
0171:
0172: public SpreadsheetOutput getOutput() {
0173: return output;
0174: }
0175:
0176: public XmlGen() throws Exception {
0177: XPathFactory factory = XPathFactory.newInstance();
0178: this .xpath = factory.newXPath();
0179: }
0180:
0181: public void run() throws Exception {
0182: if (isBlank(inputDirectoryPath)) {
0183: throw new RuntimeException(
0184: "No input directory was specified.");
0185: }
0186: if (isBlank(outputDirectoryPath)) {
0187: outputDirectoryPath = inputDirectoryPath + "/xmlgen-output";
0188: }
0189: File inputDirectory = new File(inputDirectoryPath);
0190: if (!inputDirectory.exists()) {
0191: throw new FileNotFoundException(
0192: "Could not locate the given input directory '"
0193: + inputDirectoryPath + "'");
0194: } else if (!inputDirectory.isDirectory()) {
0195: throw new RuntimeException(
0196: "Given input directory is not a directory '"
0197: + inputDirectoryPath + "'");
0198: } else if (!inputDirectory.canRead()) {
0199: throw new RuntimeException("Not permitted to read from '"
0200: + inputDirectoryPath
0201: + "'. Please check permissions.");
0202: }
0203: indexInput(inputDirectory);
0204:
0205: File outputDirectory = new File(outputDirectoryPath);
0206: if (!outputDirectory.exists()) {
0207: if (!outputDirectory.mkdirs()) {
0208: throw new RuntimeException(
0209: "Failed to create the output directory '"
0210: + outputDirectoryPath + "'");
0211: }
0212: }
0213: if (!outputDirectory.isDirectory()) {
0214: throw new RuntimeException(
0215: "Given output directory is not a directory '"
0216: + outputDirectoryPath + "'");
0217: } else if (!outputDirectory.canWrite()) {
0218: throw new RuntimeException("Not permitted to write to '"
0219: + outputDirectoryPath
0220: + "'. Please check permissions.");
0221: }
0222: generateRules(inputDirectory, outputDirectory);
0223: }
0224:
0225: public static void usage() {
0226: String usage = "\nusage: java [-java options] org.kuali.workflow.tools.xml.XmlGen [-options] input-directory\n"
0227: + "\n"
0228: + "Generate KEW XML from the files located in the given input directory. All files used for input must end with a .xml extension.\n"
0229: + "There will be 2 resulting files in the output directory:\n"
0230: + " Rules.xml - contains the rules that were generated from the spreadsheet\n"
0231: + " Workgroups.xml - contains the workgroups that were used in the rules on the spreadsheet\n"
0232: + "\n"
0233: + "Where java options include options passed to the java runtime (most importantly -classpath or -cp)\n"
0234: + "\n"
0235: + "The options for XmlGen include:\n"
0236: + " -h display this help message\n"
0237: + " -helper the fully qualified classname for the org.kuali.workflow.tools.xml.XmlGenHelper to use to generate rule descriptions and field names\n"
0238: + " -o the directory to place the output files into. By default, they are created in a subdirectory of the given directory named 'xmlgen-output'";
0239: System.err.println(usage);
0240: }
0241:
0242: protected void indexInput(File inputDirectory) throws Exception {
0243: File[] xmlFiles = inputDirectory
0244: .listFiles(new XmlFilenameFilter());
0245: List<File> sortedFiles = new ArrayList<File>();
0246: sortedFiles.addAll(Arrays.asList(xmlFiles));
0247: Collections.sort(sortedFiles, new FilenameComparator());
0248: for (File xmlFile : xmlFiles) {
0249: InputStream is = new BufferedInputStream(
0250: new FileInputStream(xmlFile));
0251: try {
0252: indexWorkflowXmlFile(is);
0253: } finally {
0254: is.close();
0255: }
0256: }
0257: }
0258:
0259: protected void indexWorkflowXmlFile(InputStream inputStream)
0260: throws Exception {
0261: Document document = DocumentBuilderFactory.newInstance()
0262: .newDocumentBuilder().parse(
0263: new InputSource(inputStream));
0264: if (!(Boolean) xpath.evaluate("/data", document,
0265: XPathConstants.BOOLEAN)) {
0266: return;
0267: }
0268: NodeList attributeNodes = (NodeList) xpath.evaluate(
0269: "/data/ruleAttributes/ruleAttribute", document,
0270: XPathConstants.NODESET);
0271: if (attributeNodes != null) {
0272: for (int index = 0; index < attributeNodes.getLength(); index++) {
0273: Element attributeElem = (Element) attributeNodes
0274: .item(index);
0275: indexAttribute(attributeElem);
0276: }
0277: }
0278: NodeList templateNodes = (NodeList) xpath.evaluate(
0279: "/data/ruleTemplates/ruleTemplate", document,
0280: XPathConstants.NODESET);
0281: if (templateNodes != null) {
0282: for (int index = 0; index < templateNodes.getLength(); index++) {
0283: Element templateElem = (Element) templateNodes
0284: .item(index);
0285: indexTemplate(templateElem);
0286: }
0287: }
0288: NodeList docTypeNodes = (NodeList) xpath.evaluate(
0289: "/data/documentTypes/documentType", document,
0290: XPathConstants.NODESET);
0291: if (docTypeNodes != null) {
0292: for (int index = 0; index < docTypeNodes.getLength(); index++) {
0293: Element docTypeElem = (Element) docTypeNodes
0294: .item(index);
0295: indexDocumentType(docTypeElem);
0296: }
0297: }
0298: }
0299:
0300: protected void indexAttribute(Element attributeElem)
0301: throws Exception {
0302: Attribute attribute = new Attribute(xmlGenHelper);
0303: String name = getChildNodeTextValue(attributeElem, "name", true);
0304: attribute.setName(name);
0305: String className = getChildNodeTextValue(attributeElem,
0306: "className", true);
0307: attribute.setClassName(className);
0308: NodeList routingConfig = (NodeList) xpath.evaluate(
0309: "./routingConfig", attributeElem,
0310: XPathConstants.NODESET);
0311: if (!isEmpty(routingConfig)) {
0312: if (routingConfig.getLength() > 1) {
0313: throw new RuntimeException(
0314: "More than one routing config was found on the given xml attribute '"
0315: + name + "'");
0316: }
0317: attribute.setXmlConfigData(getXmlContent(routingConfig
0318: .item(0)));
0319: }
0320: attributes.put(name, attribute);
0321: }
0322:
0323: protected void indexTemplate(Element templateElem) throws Exception {
0324: Template template = new Template();
0325: String name = getChildNodeTextValue(templateElem, "name", true);
0326: template.setName(name);
0327: NodeList attributeNodes = (NodeList) xpath.evaluate(
0328: "./attributes/attribute", templateElem,
0329: XPathConstants.NODESET);
0330: if (attributeNodes != null) {
0331: for (int index = 0; index < attributeNodes.getLength(); index++) {
0332: Element attributeElem = (Element) attributeNodes
0333: .item(index);
0334: String attributeName = getChildNodeTextValue(
0335: attributeElem, "name", true);
0336: Attribute attribute = attributes.get(attributeName);
0337: if (attribute == null) {
0338: throw new RuntimeException(
0339: "Could not locate rule attribute with name '"
0340: + attributeName
0341: + "' referenced from rule template '"
0342: + name + "'");
0343: }
0344: template.getAttributes().add(attribute);
0345: }
0346: }
0347: templates.put(name, template);
0348: }
0349:
0350: protected void indexDocumentType(Element docTypeElem)
0351: throws Exception {
0352: DocumentType documentType = new DocumentType();
0353: documentType.setName(getChildNodeTextValue(docTypeElem, "name",
0354: true));
0355: NodeList routeNodes = (NodeList) xpath.evaluate(
0356: "./routeNodes/*", docTypeElem, XPathConstants.NODESET);
0357: if (isEmpty(routeNodes)) {
0358: String parentName = getChildNodeTextValue(docTypeElem,
0359: "parent", false);
0360: if (!isBlank(parentName)) {
0361: DocumentType parentDocumentType = documentTypes
0362: .get(parentName);
0363: if (parentDocumentType == null) {
0364: throw new RuntimeException(
0365: "Could not locate parent document type with the name '"
0366: + parentName
0367: + "' for child document type '"
0368: + documentType.getName() + "'");
0369: }
0370: documentType.setParent(parentDocumentType);
0371: }
0372: } else {
0373: for (int index = 0; index < routeNodes.getLength(); index++) {
0374: Element routeNodeNode = (Element) routeNodes
0375: .item(index);
0376: String nodeName = getAttributeValue(routeNodeNode,
0377: "name", true);
0378: RouteNode routeNode = new RouteNode();
0379: routeNode.setName(nodeName);
0380: String ruleTemplateName = getChildNodeTextValue(
0381: routeNodeNode, "ruleTemplate", false);
0382: if (!isBlank(ruleTemplateName)) {
0383: Template template = templates.get(ruleTemplateName);
0384: if (template == null) {
0385: throw new RuntimeException(
0386: "Could not locate rule template with name '"
0387: + ruleTemplateName
0388: + "' for node '" + nodeName
0389: + "'");
0390: }
0391: routeNode.setTemplate(template);
0392: }
0393: documentType.getNodes().add(routeNode);
0394: }
0395: }
0396: documentTypes.put(documentType.getName(), documentType);
0397: }
0398:
0399: protected void generateRules(File inputDirectory,
0400: File outputDirectory) throws Exception {
0401: File[] xmlFiles = inputDirectory
0402: .listFiles(new XmlFilenameFilter());
0403: List<File> sortedFiles = new ArrayList<File>();
0404: sortedFiles.addAll(Arrays.asList(xmlFiles));
0405: Collections.sort(sortedFiles, new FilenameComparator());
0406: for (File xmlFile : xmlFiles) {
0407: InputStream is = new BufferedInputStream(
0408: new FileInputStream(xmlFile));
0409: try {
0410: processSpreadsheetXmlFile(is);
0411: } finally {
0412: is.close();
0413: }
0414: }
0415: writeXmlOutput(output, outputDirectory);
0416: }
0417:
0418: protected void processSpreadsheetXmlFile(InputStream is)
0419: throws Exception {
0420: Document document = DocumentBuilderFactory.newInstance()
0421: .newDocumentBuilder().parse(new InputSource(is));
0422: if (!(Boolean) xpath.evaluate("/Workbook", document,
0423: XPathConstants.BOOLEAN)) {
0424: return;
0425: }
0426: NodeList worksheets = (NodeList) xpath
0427: .evaluate("/Workbook/Worksheet", document,
0428: XPathConstants.NODESET);
0429: for (int index = 0; index < worksheets.getLength(); index++) {
0430: processWorksheet((Element) worksheets.item(index));
0431: }
0432: }
0433:
0434: protected void processWorksheet(Element worksheetElem)
0435: throws Exception {
0436: DocumentType documentType = null;
0437: RouteNode routeNode = null;
0438: List<String> columnHeaders = null;
0439: int columnHeadersRowIndex = -1;
0440: List<List<String>> dataRows = new ArrayList<List<String>>();
0441: List<String> currentDataRow = null;
0442: NodeList rows = (NodeList) xpath.evaluate("./Table/Row",
0443: worksheetElem, XPathConstants.NODESET);
0444: outerRow: for (int rowIndex = 0; rowIndex < rows.getLength(); rowIndex++) {
0445: Element rowElem = (Element) rows.item(rowIndex);
0446: NodeList childNodes = rowElem.getChildNodes();
0447: List<Element> cellNodes = new ArrayList<Element>();
0448: for (int childIndex = 0; childIndex < childNodes
0449: .getLength(); childIndex++) {
0450: Node childNode = childNodes.item(childIndex);
0451: if (Node.ELEMENT_NODE == childNode.getNodeType()
0452: && childNode.getNodeName().equals("Cell")) {
0453: cellNodes.add((Element) childNode);
0454: }
0455: }
0456: int currentSSIndex = 0;
0457: for (int columnIndex = 0; columnIndex < cellNodes.size(); columnIndex++) {
0458: Element column = cellNodes.get(columnIndex);
0459: String ssIndex = column.getAttribute("ss:Index");
0460: String columnValue = getChildNodeTextValue(column,
0461: "Data", false);
0462: if (columnValue != null) {
0463: columnValue = columnValue.trim();
0464: }
0465: if (documentType == null) {
0466: if (isBlank(columnValue)) {
0467: continue outerRow;
0468: }
0469: documentType = documentTypes.get(columnValue);
0470: if (documentType == null) {
0471: System.out
0472: .println("Not processing sheet. No document type with name '"
0473: + columnValue + "'");
0474: return;
0475: }
0476: continue outerRow;
0477: } else if (routeNode == null) {
0478: if (isBlank(columnValue)) {
0479: continue outerRow;
0480: }
0481: routeNode = documentType.getNodeByName(columnValue);
0482: if (routeNode == null) {
0483: throw new RuntimeException(
0484: "Failed to identify node with name '"
0485: + columnValue
0486: + "' on Document Type named '"
0487: + documentType.getName() + "'");
0488: }
0489: continue outerRow;
0490: } else if (columnHeaders == null) {
0491: if (isBlank(columnValue)) {
0492: continue outerRow;
0493: }
0494: columnHeaders = new ArrayList<String>();
0495: columnHeadersRowIndex = rowIndex;
0496: }
0497: if (columnHeadersRowIndex == rowIndex) {
0498: if (columnValue == null) {
0499: columnValue = "";
0500: }
0501: columnHeaders.add(columnValue);
0502: } else {
0503: if (columnIndex == 0) {
0504: currentDataRow = new ArrayList<String>();
0505: }
0506: if (!isBlank(ssIndex)) {
0507: int ssIndexInt = Integer.parseInt(ssIndex);
0508: while (currentSSIndex < (ssIndexInt - 1)) {
0509: currentDataRow.add(null);
0510: currentSSIndex++;
0511: }
0512: }
0513: currentDataRow.add(columnValue);
0514: }
0515: currentSSIndex++;
0516: }
0517: System.out.println("processing row: " + rowIndex);
0518: if (currentDataRow != null) {
0519: dataRows.add(currentDataRow);
0520: }
0521: currentDataRow = null;
0522: }
0523: if (!dataRows.isEmpty()) {
0524: removeEmptyRows(dataRows);
0525: int numberOfExtensionValues = getNumberOfExtensionValues(routeNode
0526: .getTemplate());
0527: for (List<String> dataRow : dataRows) {
0528: Rule rule = new Rule();
0529: rule.setDocumentType(documentType);
0530: rule.setTemplate(routeNode.getTemplate());
0531: String actionRequestedCode = "A";
0532: String workgroupName = null;
0533: List<String> users = new ArrayList<String>();
0534: int attributeIndex = 0;
0535: int nonAttributeColumns = -1;
0536: for (int index = 0; index < columnHeaders.size(); index++) {
0537: String columnHeader = columnHeaders.get(index);
0538: String columnValue = null;
0539: if (index < dataRow.size()) {
0540: columnValue = dataRow.get(index);
0541: }
0542: if (columnHeader.equalsIgnoreCase(IGNORE_PREVIOUS)) {
0543: if ("Y".equalsIgnoreCase(columnValue)
0544: || "T".equalsIgnoreCase(columnValue)
0545: || Boolean.valueOf(columnValue)) {
0546: rule.setIgnorePrevious(true);
0547: } else {
0548: rule.setIgnorePrevious(false);
0549: }
0550: } else if (columnHeader
0551: .equalsIgnoreCase(ACTION_REQUESTED)) {
0552: actionRequestedCode = processActionRequestedCode(columnValue);
0553: } else if (columnHeader
0554: .equalsIgnoreCase(DESCRIPTION)) {
0555: rule.setDescription(columnValue);
0556: } else if (columnHeader.equalsIgnoreCase(COMMENT)) {
0557: continue;
0558: } else if (attributeIndex >= numberOfExtensionValues) {
0559: if (nonAttributeColumns == -1) {
0560: nonAttributeColumns = index;
0561: // first column after attributes is the workgroup
0562: workgroupName = columnValue;
0563: } else {
0564: if (columnValue != null) {
0565: users.add(columnValue);
0566: }
0567: }
0568: } else {
0569: addExtension(attributeIndex++, rule,
0570: columnValue);
0571: }
0572: }
0573: if (isBlank(actionRequestedCode)) {
0574: actionRequestedCode = "A";
0575: }
0576: addResponsibility(rule, workgroupName, users,
0577: actionRequestedCode);
0578: if (isBlank(rule.getDescription())) {
0579: rule.setDescription(generateRuleDescription(rule));
0580: }
0581: output.getRules().add(rule);
0582: }
0583: }
0584: }
0585:
0586: protected int getNumberOfExtensionValues(Template template) {
0587: int num = 0;
0588: for (Attribute attribute : template.getAttributes()) {
0589: num += attribute.getFieldNames().size();
0590: }
0591: return num;
0592: }
0593:
0594: protected void removeEmptyRows(List<List<String>> dataRows) {
0595: int totalSize = dataRows.size();
0596: int emptyRows = 0;
0597: outer: for (Iterator<List<String>> iterator = dataRows
0598: .iterator(); iterator.hasNext();) {
0599: List<String> row = iterator.next();
0600: for (String data : row) {
0601: if (!isBlank(data)) {
0602: continue outer;
0603: }
0604: }
0605: iterator.remove();
0606: emptyRows++;
0607: }
0608: System.out.println("Removed " + emptyRows
0609: + " empty rows out of a total of " + totalSize
0610: + " rows.");
0611: }
0612:
0613: protected String processActionRequestedCode(String columnValue) {
0614: if (isBlank(columnValue)) {
0615: return null;
0616: } else if ("C".equalsIgnoreCase(columnValue)
0617: || "complete".equalsIgnoreCase(columnValue)) {
0618: return "C";
0619: } else if ("A".equalsIgnoreCase(columnValue)
0620: || "approve".equalsIgnoreCase(columnValue)) {
0621: return "A";
0622: } else if ("K".equalsIgnoreCase(columnValue)
0623: || "acknowledge".equalsIgnoreCase(columnValue)
0624: || "ack".equalsIgnoreCase(columnValue)) {
0625: return "K";
0626: } else if ("F".equalsIgnoreCase(columnValue)
0627: || "fyi".equalsIgnoreCase(columnValue)) {
0628: return "F";
0629: } else {
0630: throw new RuntimeException(
0631: "Could not determine the action requested code for the given value '"
0632: + columnValue + "'");
0633: }
0634: }
0635:
0636: protected void addExtension(int attributeIndex, Rule rule,
0637: String value) {
0638: int tempIndex = 0;
0639: for (Attribute attribute : rule.getTemplate().getAttributes()) {
0640: for (String fieldName : attribute.getFieldNames()) {
0641: if (tempIndex++ == attributeIndex) {
0642: RuleExtension ruleExtension = rule
0643: .getRuleExtensionForAttribute(attribute
0644: .getName());
0645: if (ruleExtension == null) {
0646: ruleExtension = new RuleExtension();
0647: ruleExtension.setAttribute(attribute);
0648: ruleExtension.setTemplate(rule.getTemplate());
0649: rule.getRuleExtensions().add(ruleExtension);
0650: }
0651: RuleExtensionValue ruleExtensionValue = new RuleExtensionValue();
0652: ruleExtensionValue.setKey(fieldName);
0653: ruleExtensionValue.setValue(value);
0654: ruleExtension.getExtensionValues().add(
0655: ruleExtensionValue);
0656: }
0657: }
0658: }
0659: }
0660:
0661: protected void addResponsibility(Rule rule, String workgroupName,
0662: List<String> users, String actionRequestedCode) {
0663:
0664: if (workgroupName != null) {
0665: Responsibility responsibility = new Responsibility();
0666: responsibility.setActionRequested(actionRequestedCode);
0667: Workgroup workgroup = output.getWorkgroups().get(
0668: workgroupName);
0669: if (workgroup != null && workgroup.getMembers().isEmpty()
0670: && !users.isEmpty()) {
0671: workgroup.setMembers(users);
0672: } else if (workgroup == null) {
0673: workgroup = new Workgroup();
0674: workgroup.setName(workgroupName);
0675: workgroup.setMembers(users);
0676: output.getWorkgroups().put(workgroupName, workgroup);
0677: }
0678: responsibility.setWorkgroup(workgroup);
0679: rule.getResponsibilities().add(responsibility);
0680: } else if (!users.isEmpty()) {
0681: for (String user : users) {
0682: Responsibility responsibility = new Responsibility();
0683: responsibility.setActionRequested(actionRequestedCode);
0684: responsibility.setUser(user);
0685: rule.getResponsibilities().add(responsibility);
0686: }
0687: } else {
0688: throw new RuntimeException(
0689: "No workgroup or users defined on the Rule. "
0690: + rule.toString());
0691: }
0692:
0693: }
0694:
0695: protected String generateRuleDescription(Rule rule) {
0696: if (xmlGenHelper != null) {
0697: String description = xmlGenHelper
0698: .generateRuleDescription(rule);
0699: if (!isBlank(description)) {
0700: return description;
0701: }
0702: }
0703: // generate the default description
0704: String defaultDescription = rule.getDocumentType().getName();
0705: for (RuleExtension extension : rule.getRuleExtensions()) {
0706: for (RuleExtensionValue value : extension
0707: .getExtensionValues()) {
0708: defaultDescription += ", " + value.getKey() + ": "
0709: + value.getValue();
0710: }
0711: }
0712: if (rule.getResponsibilities().size() > 0
0713: && rule.getResponsibilities().get(0).getWorkgroup() != null) {
0714: defaultDescription += " for "
0715: + rule.getResponsibilities().get(0).getWorkgroup()
0716: .getName();
0717: }
0718: return defaultDescription;
0719: }
0720:
0721: protected void writeXmlOutput(SpreadsheetOutput output,
0722: File outputDirectory) throws Exception {
0723: writeWorkgroupXmlOutput(output, outputDirectory);
0724: writeRuleXmlOutput(output, outputDirectory);
0725: }
0726:
0727: protected void writeWorkgroupXmlOutput(SpreadsheetOutput output,
0728: File outputDirectory) throws Exception {
0729: if (!output.getWorkgroups().isEmpty()) {
0730: validateWorkgroups(output);
0731: File workgroupFile = new File(outputDirectory,
0732: "Workgroups.xml");
0733: workgroupFile.createNewFile();
0734: Document document = DocumentBuilderFactory.newInstance()
0735: .newDocumentBuilder().newDocument();
0736: Element dataElement = createDataElement(document);
0737: document.appendChild(dataElement);
0738: Element workgroupsElement = document
0739: .createElement("workgroups");
0740: workgroupsElement.setAttribute("xmlns",
0741: "ns:workflow/Workgroup");
0742: workgroupsElement.setAttribute("xsi:schemaLocation",
0743: "ns:workflow/Workgroup resource:Workgroup");
0744: dataElement.appendChild(workgroupsElement);
0745: for (Workgroup workgroup : output.getWorkgroups().values()) {
0746: Element workgroupElement = document
0747: .createElement("workgroup");
0748: workgroupElement.setAttribute("allowOverwrite", "true");
0749: workgroupsElement.appendChild(workgroupElement);
0750: Element workgroupNameElement = document
0751: .createElement("workgroupName");
0752: workgroupElement.appendChild(workgroupNameElement);
0753: workgroupNameElement.appendChild(document
0754: .createTextNode(workgroup.getName()));
0755: Element membersElement = document
0756: .createElement("members");
0757: workgroupElement.appendChild(membersElement);
0758: for (String member : workgroup.getMembers()) {
0759: Element memberElement = document
0760: .createElement("authenticationId");
0761: membersElement.appendChild(memberElement);
0762: memberElement.appendChild(document
0763: .createTextNode(member));
0764: }
0765: }
0766: TransformerFactory factory = TransformerFactory
0767: .newInstance();
0768: Transformer transformer = factory.newTransformer();
0769: transformer.setOutputProperty(OutputKeys.INDENT, "yes");
0770: transformer.transform(new DOMSource(document),
0771: new StreamResult(
0772: new FileOutputStream(workgroupFile)));
0773: }
0774: }
0775:
0776: protected void validateWorkgroups(SpreadsheetOutput output) {
0777: for (Workgroup workgroup : output.getWorkgroups().values()) {
0778: if (isBlank(workgroup.getName())) {
0779: throw new RuntimeException(
0780: "Found a workgroup in the output without a name!");
0781: }
0782: if (workgroup.getMembers() == null
0783: || workgroup.getMembers().isEmpty()) {
0784: throw new RuntimeException(
0785: "No members declared for workgroup '"
0786: + workgroup.getName() + "'");
0787: }
0788: Set<String> foundMembers = new HashSet<String>();
0789: for (Iterator iterator = workgroup.getMembers().iterator(); iterator
0790: .hasNext();) {
0791: String member = (String) iterator.next();
0792: if (foundMembers.contains(member)) {
0793: iterator.remove();
0794: } else {
0795: foundMembers.add(member);
0796: }
0797: }
0798: }
0799: }
0800:
0801: protected void writeRuleXmlOutput(SpreadsheetOutput output,
0802: File outputDirectory) throws Exception {
0803: if (!output.getRules().isEmpty()) {
0804: File ruleFile = new File(outputDirectory, "Rules.xml");
0805: ruleFile.createNewFile();
0806: Document document = DocumentBuilderFactory.newInstance()
0807: .newDocumentBuilder().newDocument();
0808: Element dataElement = createDataElement(document);
0809: document.appendChild(dataElement);
0810: Element rulesElement = document.createElement("rules");
0811: rulesElement.setAttribute("xmlns", "ns:workflow/Rule");
0812: rulesElement.setAttribute("xsi:schemaLocation",
0813: "ns:workflow/Rule resource:Rule");
0814: dataElement.appendChild(rulesElement);
0815: for (Rule rule : output.getRules()) {
0816: Element ruleElement = document.createElement("rule");
0817: rulesElement.appendChild(ruleElement);
0818: Element documentTypeElem = document
0819: .createElement("documentType");
0820: Element ruleTemplateElem = document
0821: .createElement("ruleTemplate");
0822: Element descriptionElem = document
0823: .createElement("description");
0824: Element ignorePreviousElem = document
0825: .createElement("ignorePrevious");
0826: Element ruleExtensionsElem = document
0827: .createElement("ruleExtensions");
0828: Element responsibilitiesElem = document
0829: .createElement("responsibilities");
0830:
0831: ruleElement.appendChild(documentTypeElem);
0832: ruleElement.appendChild(ruleTemplateElem);
0833: ruleElement.appendChild(descriptionElem);
0834: ruleElement.appendChild(ignorePreviousElem);
0835: if (rule.getRuleExtensions() != null
0836: && !rule.getRuleExtensions().isEmpty()) {
0837: ruleElement.appendChild(ruleExtensionsElem);
0838: }
0839: ruleElement.appendChild(responsibilitiesElem);
0840:
0841: documentTypeElem.appendChild(document
0842: .createTextNode(rule.getDocumentType()
0843: .getName()));
0844: ruleTemplateElem.appendChild(document
0845: .createTextNode(rule.getTemplate().getName()));
0846: descriptionElem.appendChild(document
0847: .createTextNode(rule.getDescription()));
0848: ignorePreviousElem.appendChild(document
0849: .createTextNode(rule.getIgnorePrevious()
0850: .toString()));
0851:
0852: if (rule.getRuleExtensions() != null) {
0853: for (RuleExtension extension : rule
0854: .getRuleExtensions()) {
0855: if (!ruleExtensionHasValues(extension)) {
0856: continue;
0857: }
0858: Element ruleExtensionElem = document
0859: .createElement("ruleExtension");
0860: ruleExtensionsElem
0861: .appendChild(ruleExtensionElem);
0862:
0863: Element attributeElem = document
0864: .createElement("attribute");
0865: ruleExtensionElem.appendChild(attributeElem);
0866: attributeElem.appendChild(document
0867: .createTextNode(extension
0868: .getAttribute().getName()));
0869:
0870: Element ruleExtensionTemplateElem = document
0871: .createElement("ruleTemplate");
0872: ruleExtensionElem
0873: .appendChild(ruleExtensionTemplateElem);
0874: ruleExtensionTemplateElem.appendChild(document
0875: .createTextNode(extension.getTemplate()
0876: .getName()));
0877:
0878: Element ruleExtensionValuesElem = document
0879: .createElement("ruleExtensionValues");
0880: ruleExtensionElem
0881: .appendChild(ruleExtensionValuesElem);
0882:
0883: for (RuleExtensionValue extensionValue : extension
0884: .getExtensionValues()) {
0885: if (isBlank(extensionValue.getValue())) {
0886: continue;
0887: }
0888: Element ruleExtensionValueElem = document
0889: .createElement("ruleExtensionValue");
0890: ruleExtensionValuesElem
0891: .appendChild(ruleExtensionValueElem);
0892:
0893: Element keyElem = document
0894: .createElement("key");
0895: keyElem.appendChild(document
0896: .createTextNode(extensionValue
0897: .getKey()));
0898: ruleExtensionValueElem.appendChild(keyElem);
0899:
0900: Element valueElem = document
0901: .createElement("value");
0902: valueElem.appendChild(document
0903: .createTextNode(extensionValue
0904: .getValue()));
0905: ruleExtensionValueElem
0906: .appendChild(valueElem);
0907: }
0908: }
0909: }
0910:
0911: for (Responsibility responsibility : rule
0912: .getResponsibilities()) {
0913: Element responsibilityElem = document
0914: .createElement("responsibility");
0915: responsibilitiesElem
0916: .appendChild(responsibilityElem);
0917:
0918: if (responsibility.getWorkgroup() != null) {
0919: Element workgroupElem = document
0920: .createElement("workgroup");
0921: workgroupElem.appendChild(document
0922: .createTextNode(responsibility
0923: .getWorkgroup().getName()));
0924: responsibilityElem.appendChild(workgroupElem);
0925: } else {
0926: Element userElem = document
0927: .createElement("user");
0928: userElem.appendChild(document
0929: .createTextNode(responsibility
0930: .getUser()));
0931: responsibilityElem.appendChild(userElem);
0932: }
0933:
0934: Element actionRequestedElem = document
0935: .createElement("actionRequested");
0936: actionRequestedElem.appendChild(document
0937: .createTextNode(responsibility
0938: .getActionRequested()));
0939: responsibilityElem.appendChild(actionRequestedElem);
0940:
0941: Element priorityElem = document
0942: .createElement("priority");
0943: priorityElem.appendChild(document
0944: .createTextNode("1"));
0945: responsibilityElem.appendChild(priorityElem);
0946: }
0947: }
0948: Transformer transformer = TransformerFactory.newInstance()
0949: .newTransformer();
0950: transformer.setOutputProperty(OutputKeys.INDENT, "yes");
0951: transformer.transform(new DOMSource(document),
0952: new StreamResult(new FileOutputStream(ruleFile)));
0953: }
0954:
0955: }
0956:
0957: protected boolean ruleExtensionHasValues(RuleExtension ruleExtension) {
0958: for (RuleExtensionValue extensionValue : ruleExtension
0959: .getExtensionValues()) {
0960: if (!isBlank(extensionValue.getValue())) {
0961: return true;
0962: }
0963: }
0964: return false;
0965: }
0966:
0967: protected Element createDataElement(Document document) {
0968: Element dataElement = document.createElement("data");
0969: dataElement.setAttribute("xmlns", "ns:workflow");
0970: dataElement.setAttribute("xmlns:xsi",
0971: "http://www.w3.org/2001/XMLSchema-instance");
0972: dataElement.setAttribute("xsi:schemaLocation",
0973: "ns:workflow resource:WorkflowData");
0974: return dataElement;
0975: }
0976:
0977: protected String getAttributeValue(Element parentElement,
0978: String attributeName, boolean required) {
0979: String attributeValue = parentElement
0980: .getAttribute(attributeName);
0981: if (isBlank(attributeValue)) {
0982: if (required) {
0983: throw new RuntimeException(
0984: "Could not locate value for attribute '"
0985: + attributeName + "' on element '"
0986: + parentElement.getTagName() + "'");
0987: }
0988: return null;
0989: }
0990: return attributeValue;
0991: }
0992:
0993: protected String getChildNodeTextValue(Element parentElement,
0994: String childElementName, boolean required) {
0995: for (int index = 0; index < parentElement.getChildNodes()
0996: .getLength(); index++) {
0997: if (parentElement.getChildNodes().item(index).getNodeType() == Node.ELEMENT_NODE) {
0998: Element childElement = (Element) parentElement
0999: .getChildNodes().item(index);
1000: if (childElement.getTagName().equals(childElementName)) {
1001: String textValue = getTextContent(childElement);
1002: if (isBlank(textValue)) {
1003: if (required) {
1004: throw new RuntimeException(
1005: "Could not locate value for element '"
1006: + childElementName
1007: + "' in parent element '"
1008: + parentElement
1009: .getTagName() + "'");
1010: }
1011: return null;
1012: }
1013: return textValue;
1014: }
1015: }
1016: }
1017: if (required) {
1018: throw new RuntimeException(
1019: "Could not locate a child element named '"
1020: + childElementName
1021: + "' in parent element '"
1022: + parentElement.getTagName() + "'");
1023: }
1024: return null;
1025: }
1026:
1027: protected String getTextContent(Element element) {
1028: NodeList children = element.getChildNodes();
1029: Node node = children.item(0);
1030: return node.getNodeValue();
1031: }
1032:
1033: protected String getXmlContent(Node node) throws Exception {
1034: Source source = new DOMSource(node);
1035: StringWriter writer = new StringWriter();
1036: Result result = new StreamResult(writer);
1037: Transformer transformer = TransformerFactory.newInstance()
1038: .newTransformer();
1039: transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
1040: "yes");
1041: transformer.transform(source, result);
1042: return writer.toString();
1043: }
1044:
1045: public static boolean isBlank(String string) {
1046: return string == null || string.trim().equals("");
1047: }
1048:
1049: public static boolean isEmpty(NodeList nodes) {
1050: return nodes == null || nodes.getLength() == 0;
1051: }
1052:
1053: private class XmlFilenameFilter implements FilenameFilter {
1054:
1055: public boolean accept(File file, String fileName) {
1056: return fileName.endsWith(".xml");
1057: }
1058:
1059: }
1060:
1061: private class FilenameComparator implements Comparator<File> {
1062:
1063: public int compare(File file1, File file2) {
1064: return file1.getName().compareTo(file2.getName());
1065: }
1066:
1067: }
1068:
1069: }
|