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

org.hisrc.jsonix.configuration.ModulesConfiguration Maven / Gradle / Ivy

There is a newer version: 2.3.9
Show newest version
package org.hisrc.jsonix.configuration;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hisrc.jsonix.analysis.ModelInfoGraphAnalyzer;
import org.hisrc.jsonix.context.JsonixContext;
import org.hisrc.jsonix.definition.Mapping;
import org.hisrc.jsonix.definition.Module;
import org.hisrc.jsonix.definition.Modules;
import org.hisrc.jsonix.naming.StandardNaming;
import org.jgrapht.DirectedGraph;
import org.jgrapht.EdgeFactory;
import org.jgrapht.alg.StrongConnectivityInspector;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.traverse.TopologicalOrderIterator;
import org.jvnet.jaxb2_commons.xml.bind.model.MModelInfo;
import org.jvnet.jaxb2_commons.xml.bind.model.MPackageInfo;
import org.slf4j.Logger;

@XmlRootElement(name = ModulesConfiguration.LOCAL_ELEMENT_NAME)
@XmlType(propOrder = {})
public class ModulesConfiguration {

	public static final String DEFAULT_PREFIX = "jsonix";

	public static final String NAMESPACE_URI = "http://jsonix.highsource.org/customizations";

	public static final String LOCAL_ELEMENT_NAME = "modules";
	public static final QName ELEMENT_NAME = new QName(
			ModulesConfiguration.NAMESPACE_URI, LOCAL_ELEMENT_NAME,
			ModulesConfiguration.DEFAULT_PREFIX);

	private List moduleConfigurations = new LinkedList();
	private List mappingConfigurations = new LinkedList();
	private List outputConfigurations = new LinkedList();

	public List getModuleConfigurations() {
		return moduleConfigurations;
	}

	public void setModuleConfigurations(
			List moduleConfigurations) {
		this.moduleConfigurations = moduleConfigurations;
	}

	public List getMappingConfigurations() {
		return mappingConfigurations;
	}

	public void setMappingConfigurations(
			List mappingConfigurations) {
		this.mappingConfigurations = mappingConfigurations;
	}

	public List getOutputConfigurations() {
		return outputConfigurations;
	}

	public void setOutputConfigurations(
			List outputConfigurations) {
		this.outputConfigurations = outputConfigurations;
	}

	public  Modules build(JsonixContext context,
			MModelInfo modelInfo) {

		final ModelInfoGraphAnalyzer analyzer = new ModelInfoGraphAnalyzer(
				context, modelInfo);

		final List moduleConfigurations = new LinkedList(
				getModuleConfigurations());

		createModuleConfigurationsForMappingConfigurations(
				moduleConfigurations, getMappingConfigurations());

		createModuleConfigurationsForUnmappedPackages(analyzer,
				moduleConfigurations);

		assignDefaultOutputConfigurations(moduleConfigurations);

		assignMappingNamesAndIds(context, moduleConfigurations);

		return buildModules(context, modelInfo, analyzer, moduleConfigurations);
	}

	private void assignMappingNamesAndIds(JsonixContext context,
			final List moduleConfigurations) {
		// Generate ids where missing
		final Map idToMappingConfiguration = new HashMap();
		final Map> nameToMappingConfiguration = new HashMap>();

		// Set mapping name and ids
		for (final ModuleConfiguration moduleConfiguration : moduleConfigurations) {
			for (final MappingConfiguration mappingConfiguration : moduleConfiguration
					.getMappingConfigurations()) {

				assignMappingName(mappingConfiguration);

				assignMappingId(context, idToMappingConfiguration,
						nameToMappingConfiguration, mappingConfiguration);
			}
		}
	}

	private void assignMappingId(
			JsonixContext context,
			final Map idToMappingConfiguration,
			final Map> nameToMappingConfiguration,
			final MappingConfiguration mappingConfiguration) {
		final Logger logger = Validate.notNull(context).getLoggerFactory()
				.getLogger(ModulesConfiguration.class.getName());

		final String mappingName = mappingConfiguration.getName();
		String mappingId = mappingConfiguration.getId();

		if (mappingId != null) {
			// TODO throw an exception, don't try to correct
			if (idToMappingConfiguration.containsKey(mappingId)) {
				logger.error(MessageFormat
						.format("Mapping id [{0}] is already defined, generating a new mapping id.",
								mappingId));
				mappingId = null;
			}
		}
		if (mappingId != null) {
			idToMappingConfiguration.put(mappingId, mappingConfiguration);
		} else {
			List mappings = nameToMappingConfiguration
					.get(mappingName);
			if (mappings == null) {
				mappings = new ArrayList(2);
				logger.debug(MessageFormat.format(
						"Assigning id [{0}] to the mapping with name [{1}].",
						mappingName, mappingName));
				mappingId = mappingName;
				mappingConfiguration.setId(mappingId);
				mappings.add(mappingConfiguration);
				nameToMappingConfiguration.put(mappingName, mappings);
				idToMappingConfiguration.put(mappingId, mappingConfiguration);
			} else if (mappings.size() == 1) {
				logger.debug(MessageFormat
						.format("There are more than one mapping with the name [{0}] without id.",
								mappingName));
				final String mappingId0 = mappingName + "-0";
				final MappingConfiguration mappingConfiguration0 = mappings
						.get(0);
				logger.debug(MessageFormat.format(
						"Assigning id [{0}] to the mapping with name [{1}].",
						mappingId0, mappingName));
				mappingConfiguration0.setId(mappingId0);

				mappingId = mappingName + "-1";
				logger.debug(MessageFormat.format(
						"Assigning id [{0}] to the mapping with name [{1}].",
						mappingId, mappingName));
				mappingConfiguration.setId(mappingId);
				mappings.add(mappingConfiguration);
				idToMappingConfiguration.remove(mappingName);
				idToMappingConfiguration.put(mappingId0, mappingConfiguration0);
				idToMappingConfiguration.put(mappingId, mappingConfiguration);
			} else {
				mappingId = mappingName + mappings.size();
				logger.debug(MessageFormat.format(
						"Assigning id [{0}] to the mapping with name [{1}].",
						mappingId, mappingName));
				mappingConfiguration.setId(mappingId);
				mappings.add(mappingConfiguration);
				idToMappingConfiguration.put(mappingId, mappingConfiguration);
			}
		}
	}

	private String assignMappingName(
			final MappingConfiguration mappingConfiguration) {
		String mappingName = mappingConfiguration.getName();
		if (StringUtils.isBlank(mappingName)) {
			final String packageName = mappingConfiguration.getPackage();
			if (packageName != null) {
				mappingName = StringUtils.isBlank(packageName) ? "Mapping"
						: packageName.replace('.', '_');
			}
			mappingConfiguration.setName(mappingName);
		}
		return mappingName;
	}

	private  void createModuleConfigurationsForUnmappedPackages(
			final ModelInfoGraphAnalyzer analyzer,
			final List moduleConfigurations) {
		final Set packageNames = findUnmappedPackageNames(
				analyzer.getPackageNames(), moduleConfigurations);

		for (final String packageName : packageNames) {
			final MappingConfiguration mappingConfiguration = new MappingConfiguration();
			mappingConfiguration.setPackage(packageName);
			final ModuleConfiguration moduleConfiguration = new ModuleConfiguration();
			moduleConfiguration.getMappingConfigurations().add(
					mappingConfiguration);
			moduleConfigurations.add(moduleConfiguration);
		}
	}

	private void assignDefaultOutputConfigurations(
			final List moduleConfigurations) {
		final List defaultOutputConfigurations = createDefaultOutputConfigurations();
		for (final ModuleConfiguration moduleConfiguration : moduleConfigurations) {
			if (moduleConfiguration.getOutputConfigurations().isEmpty()) {
				moduleConfiguration.getOutputConfigurations().addAll(
						defaultOutputConfigurations);
			}
		}
	}

	private Set findUnmappedPackageNames(
			final Set allPackageNames,
			final List moduleConfigurations) {
		final Set packageNames = new HashSet(allPackageNames);

		final Set mappedPackagesNames = new HashSet();
		for (final ModuleConfiguration moduleConfiguration : moduleConfigurations) {
			for (final MappingConfiguration mappingConfiguration : moduleConfiguration
					.getMappingConfigurations()) {
				mappedPackagesNames.add(mappingConfiguration.getPackage());
			}
		}
		packageNames.removeAll(mappedPackagesNames);
		return packageNames;
	}

	private void createModuleConfigurationsForMappingConfigurations(
			final List moduleConfigurations,
			final List mappingConfigurations) {
		// Create one module configuration per mapping configuration
		for (final MappingConfiguration mappingConfiguration : mappingConfigurations) {
			final ModuleConfiguration moduleConfiguration = new ModuleConfiguration();
			moduleConfiguration.getMappingConfigurations().add(
					mappingConfiguration);
			moduleConfigurations.add(moduleConfiguration);
		}
	}

	private List createDefaultOutputConfigurations() {
		final List defaultOutputConfigurations = getOutputConfigurations();

		// If default output configurations are not configured, add the standard
		// configuration
		if (defaultOutputConfigurations.isEmpty()) {
			defaultOutputConfigurations.add(new OutputConfiguration(
					StandardNaming.NAMING_NAME));
		}
		return defaultOutputConfigurations;
	}

	private  Modules buildModules(JsonixContext context,
			MModelInfo modelInfo,
			final ModelInfoGraphAnalyzer analyzer,
			final List moduleConfigurations) {

		final List mappingConfigurations = getTopologicallyOrderedMappingConfigurations(
				context, moduleConfigurations);

		final Map> mappings = buildMappings(context,
				modelInfo, analyzer, mappingConfigurations);

		final List> modules = new ArrayList>(
				moduleConfigurations.size());
		for (ModuleConfiguration moduleConfiguration : moduleConfigurations) {
			final Module module = moduleConfiguration.build(analyzer,
					modelInfo, mappings);
			if (module != null) {
				modules.add(module);
			}
		}
		return new Modules(context, modelInfo, modules);
	}

	private  Map> buildMappings(
			JsonixContext context, MModelInfo modelInfo,
			final ModelInfoGraphAnalyzer analyzer,
			final List mappingConfigurations) {
		final Logger logger = Validate.notNull(context).getLoggerFactory()
				.getLogger(ModulesConfiguration.class.getName());

		final Map> mappings = new HashMap>();
		for (MappingConfiguration mappingConfiguration : mappingConfigurations) {
			final String packageName = mappingConfiguration.getPackage();
			final MPackageInfo packageInfo = analyzer.getPackageInfoMap().get(
					packageName);
			if (packageInfo == null) {
				logger.warn(MessageFormat.format(
						"Package name [{0}] could not be found.",
						Validate.notNull(packageName)));
				// throw new MissingPackageException(packageName);
			} else {
				final Mapping mapping = mappingConfiguration.build(
						context, analyzer, modelInfo, packageInfo, mappings);
				mappings.put(mappingConfiguration.getId(), mapping);
			}
		}
		return mappings;
	}

	private List getTopologicallyOrderedMappingConfigurations(
			JsonixContext context,
			final List moduleConfigurations) {
		final DirectedGraph mappingConfigurationDependencyGraph = buildMappingConfigurationDependencyGraph(
				context, moduleConfigurations);

		final StrongConnectivityInspector strongConnectivityInspector = new StrongConnectivityInspector(
				mappingConfigurationDependencyGraph);

		final List> stronglyConnectedSets = strongConnectivityInspector
				.stronglyConnectedSets();

		for (Set stronglyConnectedSet : stronglyConnectedSets) {
			if (stronglyConnectedSet.size() > 1) {
				throw new IllegalArgumentException(MessageFormat.format(
						"Mappings have the following dependency cycle: {0}",
						stronglyConnectedSet.toString()));
			}
		}

		final List mappingConfigurations = new ArrayList(
				mappingConfigurationDependencyGraph.vertexSet().size());
		for (Iterator mappingConfigurationsInTopologicalOrderIterator = new TopologicalOrderIterator(
				mappingConfigurationDependencyGraph); mappingConfigurationsInTopologicalOrderIterator
				.hasNext();) {
			mappingConfigurations
					.add(mappingConfigurationsInTopologicalOrderIterator.next());
		}
		return mappingConfigurations;
	}

	private DirectedGraph buildMappingConfigurationDependencyGraph(
			JsonixContext context,
			final List moduleConfigurations) {
		final Logger logger = Validate.notNull(context).getLoggerFactory()
				.getLogger(ModulesConfiguration.class.getName());

		final DirectedGraph mappingDependenciesGraph = new DefaultDirectedGraph(
				new EdgeFactory() {
					public Object createEdge(MappingConfiguration sourceVertex,
							MappingConfiguration targetVertex) {
						return new Object();
					};
				});

		final Map idToMappingConfiguration = new HashMap();
		final Map> nameToMappingConfiguration = new HashMap>();
		for (ModuleConfiguration moduleConfiguration : moduleConfigurations) {
			for (MappingConfiguration mappingConfiguration : moduleConfiguration
					.getMappingConfigurations()) {
				final String id = mappingConfiguration.getId();
				final String name = mappingConfiguration.getName();
				idToMappingConfiguration.put(id, mappingConfiguration);
				List mappings = nameToMappingConfiguration
						.get(name);
				if (mappings == null) {
					mappings = new ArrayList(2);
					nameToMappingConfiguration.put(name, mappings);
				}
				mappings.add(mappingConfiguration);
			}
		}

		for (ModuleConfiguration moduleConfiguration : moduleConfigurations) {
			for (MappingConfiguration mappingConfiguration : moduleConfiguration
					.getMappingConfigurations()) {
				mappingDependenciesGraph.addVertex(mappingConfiguration);
				final IncludesConfiguration includesConfiguration = mappingConfiguration
						.getIncludesConfiguration();
				if (includesConfiguration != null) {
					for (DependenciesOfMappingConfiguration dependenciesOfMappingConfiguration : includesConfiguration
							.getDependenciesOfMappingConfiguration()) {
						final String id = dependenciesOfMappingConfiguration
								.getId();
						final String name = dependenciesOfMappingConfiguration
								.getName();
						MappingConfiguration dependingMappingConfiguration = null;
						if (id != null) {
							dependingMappingConfiguration = idToMappingConfiguration
									.get(id);
							if (dependingMappingConfiguration == null) {
								throw new MissingMappingWithIdException(id);
							}
							mappingDependenciesGraph
									.addVertex(dependingMappingConfiguration);
							mappingDependenciesGraph.addEdge(
									dependingMappingConfiguration,
									mappingConfiguration);
						} else if (name != null) {
							final List dependingMappingConfigurations = nameToMappingConfiguration
									.get(name);
							if (dependingMappingConfigurations == null
									|| dependingMappingConfigurations.isEmpty()) {
								throw new MissingMappinWithNameException(name);
							} else if (dependingMappingConfigurations.size() > 1) {
								throw new AmbiguousMappingNameException(name);
							} else {
								// Ok, now the payload
								dependingMappingConfiguration = dependingMappingConfigurations
										.get(0);
								dependenciesOfMappingConfiguration
										.setId(dependingMappingConfiguration
												.getId());
							}

							mappingDependenciesGraph
									.addVertex(dependingMappingConfiguration);
							mappingDependenciesGraph.addEdge(
									dependingMappingConfiguration,
									mappingConfiguration);

						} else {
							logger.warn(MessageFormat
									.format("Either [id] or [name] must be defined in the  [{0}] element.",
											DependenciesOfMappingConfiguration.LOCAL_ELEMENT_NAME));
						}

					}
				}
			}
		}
		return mappingDependenciesGraph;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy