All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kuali.student.datadictionary.util.KradDictionaryCreator Maven / Gradle / Ivy

There is a newer version: 1.0.12
Show newest version
/**
 * Copyright 2004-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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 org.kuali.student.datadictionary.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.BreakIterator;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.apache.commons.lang.StringEscapeUtils;
import org.kuali.student.contract.model.MessageStructure;
import org.kuali.student.contract.model.ServiceContractModel;
import org.kuali.student.contract.model.XmlType;
import org.kuali.student.contract.model.util.ModelFinder;
import org.kuali.student.contract.writer.XmlWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author nwright
 */
public class KradDictionaryCreator {

	private static final Logger log = LoggerFactory
			.getLogger(KradDictionaryCreator.class);

	private ServiceContractModel model;
	private ModelFinder finder;
	private String directory;
	private String className;
	private XmlType xmlType;
	private XmlWriter gwriter;
	private XmlWriter mwriter;
	private List messageStructures;
	private boolean writeManual;
	private boolean writeGenerated;
	private String generatedFilePath;
	private String manualFilePath;

	private boolean initialized = false;

	private List enumeratedTypeList;


	public KradDictionaryCreator(String directory, ServiceContractModel model,
			String className, boolean writeManual, boolean writeGenerated, Map typeOverrides) {
		this.directory = directory;
		this.model = model;
		this.finder = new ModelFinder(this.model);
		this.className = className;
		this.xmlType = this.finder.findXmlType(className);
		if (xmlType == null) {
			throw new IllegalArgumentException(className);
		}
		this.messageStructures = this.finder.findMessageStructures(className);
		this.writeManual = writeManual;
		this.writeGenerated = writeGenerated;
		// if (this.messageStructures.isEmpty()) {
		// throw new IllegalStateException(className);
		// }
		
		initializeTypes (typeOverrides);
	}

	private void initializeTypes(Map typeOverrides) {
		
		if (KradDictionaryCreator.predefinedFieldMap == null) {
			Map map = new HashMap();
			map.put("id", "BaseKuali.id");
			map.put("key", "BaseKuali.key");
			map.put("name", "BaseKuali.name");
			map.put("descr", "BaseKuali.descr");
			map.put("plain", "BaseKuali.descr.plain");
			map.put("formatted", "BaseKuali.descr.formatted");
			map.put("desc", "BaseKuali.desc"); // r1 compatibility
			map.put("typeKey", "BaseKuali.typeKey");
			map.put("stateKey", "BaseKuali.stateKey");
			map.put("type", "BaseKuali.type"); // r1 compatibility
			map.put("state", "BaseKuali.state"); // r1 compatibility
			map.put("effectiveDate", "BaseKuali.effectiveDate");
			map.put("expirationDate", "BaseKuali.expirationDate");
			map.put("meta", "BaseKuali.meta");
			map.put("createTime", "BaseKuali.meta.createTime");
			map.put("updateTime", "BaseKuali.meta.updateTime");
			map.put("createId", "BaseKuali.meta.createId");
			map.put("updateId", "BaseKuali.meta.updateId");
			map.put("versionInd", "BaseKuali.meta.versionInd");
			// convert to lower case
			predefinedFieldMap = new HashMap(map.size());
			for (String key : map.keySet()) {
				predefinedFieldMap.put(key.toLowerCase(), map.get(key));
			}
		}
		
		
		
		if (KradDictionaryCreator.endsWithMap == null) {
			Map map = new HashMap();
			map.put("startDate", "BaseKuali.startDate");
			map.put("endDate", "BaseKuali.endDate");
			map.put("start", "BaseKuali.start");
			map.put("end", "BaseKuali.end");
			map.put("OrgId", "BaseKuali.orgId");
			map.put("OrgIds", "BaseKuali.orgId");
			map.put("PersonId", "BaseKuali.personId");
			map.put("PersonIds", "BaseKuali.personId");
			map.put("PrincipalId", "BaseKuali.principalId");
			map.put("PrincipalIds", "BaseKuali.principalId");
			map.put("CluId", "BaseKuali.cluId");
			map.put("CluIds", "BaseKuali.cluId");
			map.put("LuiId", "BaseKuali.luiId");
			map.put("LuiIds", "BaseKuali.luiId");
			map.put("AtpId", "BaseKuali.atpId");
			map.put("AtpIds", "BaseKuali.atpId");
			map.put("TermId", "BaseKuali.termId");
			map.put("TermIds", "BaseKuali.termId");
			map.put("HolidayCalendarId", "BaseKuali.holidayCalendarId");
			map.put("HolidayCalendarIds", "BaseKuali.holidayCalendarId");
			map.put("Code", "BaseKuali.code");
			// convert to lower case
			endsWithMap = new HashMap(map.size());
			for (String key : map.keySet()) {
				endsWithMap.put(key.toLowerCase(), map.get(key));
			}
		}
		
		if (KradDictionaryCreator.typeMap == null){
			Map map = new HashMap();
			map.put("String", "BaseKuali.string");
			map.put("DateTime", "BaseKuali.dateTime");
			map.put("Date", "BaseKuali.date");
			map.put("Boolean", "BaseKuali.boolean");
			map.put("Integer", "BaseKuali.integer");
			// having primitives is a bug but this will fix the issue with CluInfo
			// for now.
			map.put("int", "BaseKuali.integer");

			map.put("Long", "BaseKuali.long");
			map.put("Float", "BaseKuali.float");
			map.put("Double", "BaseKuali.double");
			
			// TODO: this should be externalized into mojo.
//			map.put("enum", "BaseKuali.complex");
			
			// convert to lower case
			typeMap = new HashMap(map.size());
			for (String key : map.keySet()) {
				typeMap.put(key.toLowerCase(), map.get(key));
			}
			
			if (typeOverrides != null) {
				// apply any overrides
				for (String key : typeOverrides.keySet()) {

					String value = typeOverrides.get(key);
					typeMap.put(key, value);

					log.warn("OVERRIDING Type Mapping '" + key + "' -> '"
					        + value + "'");
				}
			}
		}
    }

	public void write() {
		this.initXmlWriters();
		if (writeGenerated) {
			this.writeSpringHeaderOpen(gwriter);
			this.writeWarning(gwriter);
			this.writeGeneratedObjectStructure(gwriter);
			this.writeSpringHeaderClose(gwriter);
		}
		if (this.writeManual) {
			this.writeSpringHeaderOpen(mwriter);
			this.writeNote(mwriter);
			this.writeManualImports(mwriter);
			this.writeManualObjectStructure(mwriter);
			this.writeSpringHeaderClose(mwriter);
		}

		initialized = true;
	}

	private void initXmlWriters() {
		String generatedFileName = "ks-" + initUpper(className)
				+ "-dictionary-generated.xml";
		String manualFileName = "ks-" + initUpper(className)
				+ "-dictionary.xml";

		File dir = new File(this.directory);
		// System.out.indentPrintln ("Writing java class: " + fileName + " to "
		// + dir.getAbsolutePath ());

		if (!dir.exists()) {
			if (!dir.mkdirs()) {
				throw new IllegalStateException("Could not create directory "
						+ this.directory);
			}
		}

		if (writeGenerated) {
			String dirStr = this.directory + "/generated";
			File dirFile = new File(dirStr);
			if (!dirFile.exists()) {
				if (!dirFile.mkdirs()) {
					throw new IllegalStateException(
							"Could not create directory " + dirStr);
				}
			}
			try {
				PrintStream out = new PrintStream(new FileOutputStream(
						generatedFilePath = dirStr + "/"+ generatedFileName, false));
				this.gwriter = new XmlWriter(out, 0);
			} catch (FileNotFoundException ex) {
				throw new IllegalStateException(ex);
			}
		}
		if (this.writeManual) {
			String dirStr = this.directory + "/manual";
			File dirFile = new File(dirStr);
			if (!dirFile.exists()) {
				if (!dirFile.mkdirs()) {
					throw new IllegalStateException(
							"Could not create directory " + dirStr);
				}
			}
			try {
				PrintStream out = new PrintStream(new FileOutputStream(
						manualFilePath = dirStr + "/" + manualFileName, false));
				this.mwriter = new XmlWriter(out, 0);
			} catch (FileNotFoundException ex) {
				throw new IllegalStateException(ex);
			}
		}
	}

	private static String initLower(String str) {
		if (str == null) {
			return null;
		}
		if (str.length() == 0) {
			return str;
		}
		if (str.length() == 1) {
			return str.toLowerCase();
		}
		return str.substring(0, 1).toLowerCase() + str.substring(1);
	}

	private static String initUpper(String str) {
		if (str == null) {
			return null;
		}
		if (str.length() == 0) {
			return str;
		}
		if (str.length() == 1) {
			return str.toUpperCase();
		}
		return str.substring(0, 1).toUpperCase() + str.substring(1);
	}

	private void writeSpringHeaderClose(XmlWriter out) {
		out.decrementIndent();
		out.indentPrintln("");
	}

	private void writeSpringHeaderOpen(XmlWriter out) {
		out.indentPrintln("");
		out.indentPrintln("");
		out.println("");
		out.incrementIndent();
	}

	private void writeWarning(XmlWriter out) {
		out.println("");
		out.indentPrintln("");
	}

	private void writeNote(XmlWriter out) {
		out.println("");
		out.indentPrintln("");
	}

	private void writeManualImports(XmlWriter out) {
		out.writeComment("The following file gets generated during the build and gets put into the target/classes directory");
		out.indentPrintln("");
		Set imports = this.getComplexSubObjectsThatAreLists();
		if (!imports.isEmpty()) {
			out.writeComment("TODO: remove these once the jira about lists of complex objects gets fixed");
			for (String impName : imports) {
				out.indentPrintln("");
			}
		}
	}

	private Set getComplexSubObjectsThatAreLists() {
		Set list = new LinkedHashSet();
		for (MessageStructure ms : this.messageStructures) {
			switch (this.calculateCategory(ms)) {
			case LIST_OF_COMPLEX:
				String classNameToAdd = this.stripListOffEnd(ms.getType());
				// Avoid recursive calls
				if (!classNameToAdd.equalsIgnoreCase(className)) {
					list.add(classNameToAdd);
				}

				break;
			default:
				// fall though - do nothing
				break;
			}
		}
		return list;
	}

	private String stripListOffEnd(String name) {
		if (name.endsWith("List")) {
			return name.substring(0, name.length() - "List".length());
		}
		return name;
	}

	private String calcDataObjectClass(XmlType xmlType) {
		// this is those packages that are not included in the sources for
		// Enroll-API for the model
		// so the package is null but the name is the full package spec
		if (xmlType.getJavaPackage() == null
				|| xmlType.getJavaPackage().isEmpty()) {
			return xmlType.getName();
		}
		return xmlType.getJavaPackage() + "." + initUpper(xmlType.getName());
	}

	private void writeGeneratedObjectStructure(XmlWriter out) {
		// Step 1, create the abstract structure
		out.println("");
		out.indentPrintln("");
		out.indentPrintln("");
		out.incrementIndent();
		writeProperty("name", initLower(className), out);
		writeProperty("dataObjectClass", calcDataObjectClass(xmlType), out);
		writeProperty("objectLabel", calcObjectLabel(), out);
		writePropertyValue("objectDescription", xmlType.getDesc(), out);
		String titleAttribute = calcTitleAttribute();
		if (titleAttribute != null) {
			writeProperty("titleAttribute", titleAttribute, out);
		}
		out.indentPrintln("");
		List pks = calcPrimaryKeys();
		if (pks != null && !pks.isEmpty()) {
			out.incrementIndent();
			out.indentPrintln("");
			out.incrementIndent();
			for (String pk : pks) {
				addValue(pk);
			}
			out.decrementIndent();
			out.indentPrintln("");
			out.decrementIndent();
		}
		out.indentPrintln("");

		this.writeAllGeneratedAttributeRefBeans(className, null,
				new Stack(), this.messageStructures, out);

		out.indentPrintln("");

		// Step 2, loop through attributes
		this.writeGeneratedAttributeDefinitions(className, null,
				new Stack(), this.messageStructures, out);
	}

	private void writeAllGeneratedAttributeRefBeans(String currentClassName,
			String parentName, Stack parents,
			List fields, XmlWriter out) {
		if (parents.contains(currentClassName)) {
			return;
		}
		out.println("");
		out.indentPrintln("");
		out.incrementIndent();
		out.indentPrintln("");
		out.incrementIndent();
		this.writeGeneratedAttributeRefBeans(currentClassName, parentName,
				parents, fields, out, Category.PRIMITIVE);
		out.decrementIndent();
		out.indentPrintln("");
		out.decrementIndent();
		out.indentPrintln("");

		out.println("");
		out.indentPrintln("");
		out.incrementIndent();
		out.indentPrintln("");
		out.incrementIndent();
		this.writeGeneratedAttributeRefBeans(currentClassName, parentName,
				parents, fields, out, Category.COMPLEX);
		out.decrementIndent();
		out.indentPrintln("");
		out.decrementIndent();
		out.indentPrintln("");

		out.println("");
		out.indentPrintln("");
		out.incrementIndent();
		out.indentPrintln("");
		out.incrementIndent();
		this.writeGeneratedAttributeRefBeans(currentClassName, parentName,
				parents, fields, out, Category.LIST_OF_COMPLEX);
		out.decrementIndent();
		out.indentPrintln("");
		out.decrementIndent();
		out.indentPrintln("");
		out.decrementIndent();
	}

	private void addValue(String value) {
		gwriter.indentPrintln("" + value + "");
	}

	private String calcObjectLabel() {
		String label = this.className;
		if (label.endsWith("Info")) {
			label = label.substring(0, label.length() - "Info".length());
		}
		label = initUpper(label);
		return splitCamelCase(label);
	}

	// got this from
	// http://stackoverflow.com/questions/2559759/how-do-i-convert-camelcase-into-human-readable-names-in-java
	private static String splitCamelCase(String s) {
		if (s == null) {
			return null;
		}
		return s.replaceAll(String.format("%s|%s|%s",
				"(?<=[A-Z])(?=[A-Z][a-z])", "(?<=[^A-Z])(?=[A-Z])",
				"(?<=[A-Za-z])(?=[^A-Za-z])"), " ");
	}

	private enum Category {

		PRIMITIVE, COMPLEX, LIST_OF_COMPLEX, LIST_OF_PRIMITIVE, DYNAMIC_ATTRIBUTE
	};

	private Category calculateCategory(MessageStructure ms) {
		if (ms.getShortName().equals("attributes")) {
			return Category.DYNAMIC_ATTRIBUTE;
		}
		
		String childXmlTypeName = this.stripListOffEnd(ms.getType());
		XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
		if (childXmlType == null) {
			throw new IllegalStateException(childXmlTypeName);
		}
		if (ms.getType().endsWith("List")) {
			if (childXmlType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
				return Category.LIST_OF_COMPLEX;
			}
			return Category.LIST_OF_PRIMITIVE;
		}
		if (childXmlType.getPrimitive().equalsIgnoreCase(XmlType.COMPLEX)) {
			return Category.COMPLEX;
		}
		return Category.PRIMITIVE;
	}

	private void writeGeneratedAttributeRefBeans(String currentClass,
			String parentName, Stack parents,
			List fields, XmlWriter out, Category filter) {
		if (parents.contains(currentClass)) {
			return;
		}
		for (MessageStructure ms : fields) {
			Category category = this.calculateCategory(ms);
			if (!category.equals(filter)) {
				continue;
			}
			String childXmlTypeName = this.stripListOffEnd(ms.getType());
			XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
			if (childXmlType == null) {
				throw new IllegalStateException(childXmlTypeName);
			}
			String pathName = calcPathName(parentName, ms);
			String beanName = calcBeanName(pathName);
			// TODO: change this once they fix the list of complex jira
			// if (filter.equals(Category.LIST_OF_COMPLEX)) {
			// beanName = initUpper(childXmlTypeName);
			// }
			out.indentPrintln("");
			//
			// // Add complex sub-types fields
			// switch (category) {
			// case COMPLEX:
			// case LIST_OF_COMPLEX:
			// parents.push(currentClass);
			// List childFields =
			// this.finder.findMessageStructures(childXmlTypeName);
			// writeGeneratedAttributeRefBeans(childXmlTypeName, pathName,
			// parents, childFields, out, filter);
			// parents.pop();
			// }
		}
	}

	private void writeGeneratedAttributeDefinitions(String currentClassName,
			String parentName, Stack parents,
			List fields, XmlWriter out) {
		if (parents.contains(currentClassName)) {
			return;
		}
		for (MessageStructure ms : fields) {
			Category category = this.calculateCategory(ms);
			switch (category) {
			case DYNAMIC_ATTRIBUTE:
				// intentionally fall through
				ENUMERATED_TYPE:
				continue; // skip

			default:
				break;
			}
			String pathName = calcPathName(parentName, ms);
			String beanName = calcBeanName(pathName);
			String childXmlTypeName = this.stripListOffEnd(ms.getType());
			XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
			if (childXmlType == null) {
				throw new IllegalStateException(childXmlTypeName);
			}
			writeGeneratedAttributeDefinition(currentClassName, parentName,
					parents, ms, out);

			// Add complex sub-types fields
			switch (category) {
			case COMPLEX:
				// case LIST_OF_COMPLEX:
				parents.push(currentClassName);
				List childFields = this.finder
						.findMessageStructures(childXmlTypeName);
				writeGeneratedAttributeDefinitions(childXmlTypeName, pathName,
						parents, childFields, out);
				parents.pop();

				break;

			default:
				// all other cases fall through
				break;
			}
		}
	}

	private boolean shouldWriteDetails(MessageStructure ms) {
		if (predefinedFieldMap.get(ms.getShortName().toLowerCase()) == null) {
			return true;
		}
		if (ms.isOverriden()) {
			return true;
		}
		// don't write out details for predefined fields that have not been
		// overridden
		return false;
	}

	private void writeGeneratedAttributeDefinition(String currentClassName,
			String parentName, Stack parents, MessageStructure ms,
			XmlWriter out) {

		// Create the abstract field
		String pathName = calcPathName(parentName, ms);
		String beanName = calcBeanName(pathName);
		String baseKualiParentBean = this.calcBaseKualiParentBean(ms);
		out.println("");
		out.indentPrintln("");
		out.incrementIndent();
		writeProperty("name", calcSimpleName(ms), out);
		switch (this.calculateCategory(ms)) {
		case PRIMITIVE:
			if (this.shouldWriteDetails(ms)) {
				writeProperty("shortLabel", calcShortLabel(ms), out);
				writePropertyValue("summary", calcSummary(ms), out);
				writeProperty("label", calcLabel(ms), out);
				writePropertyValue("description", calcDescription(ms), out);
				if (this.calcReadOnly(ms)) {
					this.writeReadOnlyAttributeSecurity(out);
				}
				writeProperty("required", calcRequired(ms), out);
			}
			break;
		case LIST_OF_PRIMITIVE:
			// TODO: deal with once https://jira.kuali.org/browse/KULRICE-5439
			// is fixed
			// for now treat the same as List of Complex, i.e.
			// CollectionDefinition
			writeProperty("shortLabel", calcShortLabel(ms), out);
			writePropertyValue("summary", calcSummary(ms), out);
			writeProperty("label", calcLabel(ms), out);
			writeProperty("elementLabel", calcElementLabel(ms), out);
			writePropertyValue("description", calcDescription(ms), out);
			writeProperty("minOccurs", calcMinOccurs(ms), out);
			writeProperty("dataObjectClass", calcDataObjectClass(ms), out);
			break;
		case LIST_OF_COMPLEX:
			writeProperty("shortLabel", calcShortLabel(ms), out);
			writePropertyValue("summary", calcSummary(ms), out);
			writeProperty("label", calcLabel(ms), out);
			writeProperty("elementLabel", calcElementLabel(ms), out);
			writePropertyValue("description", calcDescription(ms), out);
			writeProperty("minOccurs", calcMinOccurs(ms), out);
			writeProperty("dataObjectClass", calcDataObjectClass(ms), out);
			break;
		case COMPLEX:
			writeProperty("shortLabel", calcShortLabel(ms), out);
			writePropertyValue("summary", calcSummary(ms), out);
			writeProperty("label", calcLabel(ms), out);
			writePropertyValue("description", calcDescription(ms), out);
			writeProperty("required", calcRequired(ms), out);
			writePropertyStart("dataObjectEntry", out);
			out.indentPrintln("");
			out.incrementIndent();
			writeProperty("name", calcSimpleName(ms), out);
			writeProperty("dataObjectClass", calcDataObjectClass(ms), out);
			writeProperty("objectLabel", calcLabel(ms), out);
			writePropertyValue("objectDescription", calcDescription(ms), out);

			String childXmlTypeName = this.stripListOffEnd(ms.getType());
			List childFields = this.finder
					.findMessageStructures(childXmlTypeName);
			writeAllGeneratedAttributeRefBeans(childXmlTypeName, pathName,
					parents, childFields, out);
			out.indentPrintln("");
			writePropertyEnd(out);
			break;
		default:
			throw new IllegalStateException("unknown/unhandled type "
					+ ms.getId());
		}
		out.decrementIndent();
		// TODO: implement maxoccurs
		// if (isList(pd)) {
		// addProperty("maxOccurs", "" + DictionaryConstants.UNBOUNDED, s);
		// }
		out.indentPrintln("");
	}

	private String calcDataObjectClass(MessageStructure ms) {
		XmlType msType = this.finder.findXmlType(this.stripListOffEnd(ms
				.getType()));
		return this.calcDataObjectClass(msType);
	}

	private String calcBeanName(String pathName) {
		return initUpper(className) + "." + pathName;
	}

	private String calcPathName(String parentName, MessageStructure ms) {
		String name = this.calcSimpleName(ms);
		if (parentName == null) {
			return name;
		}
		return parentName + "." + name;
	}

	private String calcSimpleName(MessageStructure ms) {
		String name = initLower(ms.getShortName());
		return name;
	}

	private boolean calcReadOnly(MessageStructure ms) {
		if (ms.getReadOnly() == null) {
			return false;
		}
		return true;
	}

	private void writeReadOnlyAttributeSecurity(XmlWriter out) {
		out.indentPrintln("");
	}

	private String calcElementLabel(MessageStructure ms) {
		String label = this.calcShortLabel(ms);
		if (label.endsWith("s")) {
			label = label.substring(0, label.length() - 1);
		}
		return label;
	}

	private String calcShortLabel(MessageStructure ms) {
		return this.splitCamelCase(initUpper(ms.getShortName()));
	}

	private String calcLabel(MessageStructure ms) {
		return ms.getName();
	}

	private String calcSummary(MessageStructure ms) {
		BreakIterator bi = BreakIterator.getSentenceInstance();
		String description = ms.getDescription();
		if (description == null) {
			return "???";
		}
		bi.setText(ms.getDescription());
		// one big sentence
		if (bi.next() == BreakIterator.DONE) {
			return ms.getDescription();
		}
		String firstSentence = description.substring(0, bi.current());
		return firstSentence;
	}

	private String calcDescription(MessageStructure ms) {
		return ms.getDescription();
	}

	private String calcMinOccurs(MessageStructure ms) {
		String required = this.calcRequired(ms);
		if ("false".equals(required)) {
			return "0";
		}
		return "1";
	}

	private String calcRequired(MessageStructure ms) {
		if (ms.getRequired() == null) {
			return "false";
		}
		if (ms.getRequired().equalsIgnoreCase("Required")) {
			return "true";
		}
		// TODO: figure out what to do if it is qualified like
		// "required on update"
		return "false";
	}

	private void writeManualObjectStructure(XmlWriter out) {
		// Step 1, create the parent bean
		out.println("");
		out.indentPrintln("");
		// Create the actual instance of the bean
		out.indentPrintln("");
		out.indentPrintln("");
		out.writeComment("insert any overrides to the generated object definitions here");
		out.indentPrintln("");

		// Step 2, loop through attributes
		this.writeManualAttributeDefinitions(className, null,
				new Stack(), this.messageStructures, out);

	}

	private void writeManualAttributeDefinitions(String currentClass,
			String parentName, Stack parents,
			List fields, XmlWriter out) {
		if (parents.contains(currentClass)) {
			return;
		}
		for (MessageStructure ms : fields) {
			Category cat = this.calculateCategory(ms);
			// skip dynamic attributes
			switch (cat) {
			case DYNAMIC_ATTRIBUTE:
				continue; // skip

			default:
				break;
			}

			String pathName = calcPathName(parentName, ms);
			String beanName = calcBeanName(pathName);
			String childXmlTypeName = this.stripListOffEnd(ms.getType());
			XmlType childXmlType = this.finder.findXmlType(childXmlTypeName);
			if (childXmlType == null) {
				throw new IllegalStateException(childXmlTypeName);
			}
			writeManualAttributeDefinition(currentClass, parentName, ms, out);

			// Add complex sub-types fields
			switch (cat) {
			case COMPLEX:
				parents.push(currentClass);
				List childFields = this.finder
						.findMessageStructures(childXmlTypeName);
				// if (childFields.isEmpty()) {
				// throw new IllegalStateException(childXmlTypeName);
				// }
				writeManualAttributeDefinitions(childXmlTypeName, pathName,
						parents, childFields, out);
				parents.pop();

				break;

			default:
				break;
			}
		}
	}

	private void writeManualAttributeDefinition(String currentClass,
			String parentName, MessageStructure ms, XmlWriter out) {

		// Create the abstract field
		String pathName = calcPathName(parentName, ms);
		String beanName = calcBeanName(pathName);
		// String baseKualiType = this.calcBaseKualiType(ms);
		// Create the actual bean instance
		out.println("");
		out.indentPrintln("");
		out.indentPrintln("");
		out.writeComment("insert any overrides to the generated attribute definitions here");
		out.indentPrintln("");
	}

	/**
	 * list of predefined fields that should map to entries in
	 * ks-base-dictionary.xml
	 */
	private static Map predefinedFieldMap = null;

	/**
	 * list of fields that if they end with the key the should be based on the
	 * entry in ks-base-dictionary.xml
	 */
	private static Map endsWithMap = null;

	
	/**
	 * list of types that if the type matches this key then it should be based
	 * on that type entry as defined in the ks-base-dictionary.xml
	 */
	private static Map typeMap = null;

	

	private String calcBaseKualiParentBean(MessageStructure ms) {
		switch (this.calculateCategory(ms)) {
		case COMPLEX:
			return "ComplexAttributeDefinition";
		case LIST_OF_COMPLEX:
			return "CollectionDefinition";
		case LIST_OF_PRIMITIVE:
			// TODO: deal with once https://jira.kuali.org/browse/KULRICE-5439
			// is fixed
			System.out
					.println("Treating list of primitives same as collection defintion: "
							+ ms.getId());
			return "CollectionDefinition";
		case PRIMITIVE:
			break;
		default:
			throw new IllegalStateException("unknown/uhandled category "
					+ ms.getId());
		}
		String name = ms.getShortName();
		String baseKualiType = predefinedFieldMap.get(name.toLowerCase());
		if (baseKualiType != null) {
			return baseKualiType;
		}

		// check ends with
		for (String key : endsWithMap.keySet()) {
			if (name.toLowerCase().endsWith(key)) {
				return endsWithMap.get(key);
			}
		}

		// now key off the type
		String type = this.stripListOffEnd(ms.getType());
		baseKualiType = typeMap.get(type.toLowerCase());
		if (baseKualiType != null) {
			return baseKualiType;
		}
		throw new IllegalStateException(
				"All primitives are supposed to be handled by a predefined type "
						+ ms.getId());
	}

	private String calcTitleAttribute() {
		MessageStructure ms = null;
		ms = this.findMessageStructure("name");
		if (ms != null) {
			return initLower(ms.getShortName());
		}
		ms = this.findMessageStructure("title");
		if (ms != null) {
			return initLower(ms.getShortName());
		}
		ms = this.findMessageStructureEndsWith("title");
		if (ms != null) {
			return initLower(ms.getShortName());
		}
		ms = this.findMessageStructureEndsWith("name");
		if (ms != null) {
			return initLower(ms.getShortName());
		}
		ms = this.findMessageStructure("key");
		if (ms != null) {
			return initLower(ms.getShortName());
		}
		ms = this.findMessageStructure("id");
		if (ms != null) {
			return initLower(ms.getShortName());
		}
		
		log.warn("XmlKradBaseDictionaryCreator: could not find a title attribute for "
						+ this.className);
		return null;
	}

	private MessageStructure findMessageStructureEndsWith(
			String shortNameEndsWith) {
		shortNameEndsWith = shortNameEndsWith.toLowerCase();
		for (MessageStructure ms : this.messageStructures) {
			if (ms.getShortName().toLowerCase().endsWith(shortNameEndsWith)) {
				return ms;
			}
		}
		return null;
	}

	private MessageStructure findMessageStructure(String shortName) {
		for (MessageStructure ms : this.messageStructures) {
			if (ms.getShortName().equalsIgnoreCase(shortName)) {
				return ms;
			}
		}
		return null;
	}

	private MessageStructure getMessageStructure(String shortName) {
		MessageStructure ms = this.findMessageStructure(shortName);
		if (ms == null) {
			throw new IllegalArgumentException(shortName);
		}
		return ms;
	}

	private List calcPrimaryKeys() {
		MessageStructure ms = null;
		ms = this.findMessageStructure("id");
		if (ms != null) {
			return Collections.singletonList(initLower(ms.getShortName()));
		}
		ms = this.findMessageStructure("key");
		if (ms != null) {
			return Collections.singletonList(initLower(ms.getShortName()));
		}
		// just use the first field
		if (this.messageStructures.size() > 0) {
			ms = this.messageStructures.get(0);
			return Collections.singletonList(ms.getShortName());
		}
		return Collections.EMPTY_LIST;
	}

	private void writePropertyStart(String propertyName, XmlWriter out) {
		out.indentPrintln("");
		out.incrementIndent();
	}

	private void writePropertyEnd(XmlWriter out) {
		out.decrementIndent();
		out.indentPrintln("");
	}

	private void writeProperty(String propertyName, String propertyValue,
			XmlWriter out) {
		out.indentPrintln("");
	}

	private void writePropertyValue(String propertyName, String propertyValue,
			XmlWriter out) {
		writePropertyStart(propertyName, out);
		out.indentPrintln("");
		// TODO: worry about the value starting on a new line i.e. is it trimmed
		// when loaded?
		out.println(escapeHtml(propertyValue));
		out.indentPrintln("");
		writePropertyEnd(out);
	}

	private String escapeHtml(String str) {
		if (str == null) {
			return null;
		}
		return StringEscapeUtils.escapeHtml(str);
	}

	private String replaceDoubleQuotes(String str) {
		if (str == null) {
			return null;
		}
		return str.replace("\"", "'");
	}

	/**
	 * Delete the files associated with this dictionary.
	 * 
	 * The use case is where an error is detected and we want to remove the file
	 * (would be size 0) from the disk.
	 * 
	 */
	public void delete() {

		close();

	}

	public void close() {
		if (initialized) {
			this.gwriter.getOut().close();
			this.mwriter.getOut().close();

			if (!new File(manualFilePath).delete())
				log.warn("failed to delete manual path: " + manualFilePath);

			if (!new File(generatedFilePath).delete())
				log.warn("failed to delete generated path: "
						+ generatedFilePath);

			initialized = false;
		}
		
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy