001package org.kuali.common.util.metainf.spring;
002
003import java.io.File;
004import java.util.ArrayList;
005import java.util.Comparator;
006import java.util.HashMap;
007import java.util.List;
008import java.util.Map;
009
010import org.kuali.common.util.metainf.model.MetaInfContext;
011import org.kuali.common.util.metainf.model.MetaInfResource;
012import org.kuali.common.util.metainf.model.MetaInfResourceFilenameComparator;
013import org.kuali.common.util.metainf.model.MetaInfResourceLocationComparator;
014import org.kuali.common.util.metainf.service.MetaInfUtils;
015import org.kuali.common.util.nullify.NullUtils;
016import org.kuali.common.util.project.ProjectUtils;
017import org.kuali.common.util.project.model.Build;
018import org.kuali.common.util.project.model.Project;
019import org.kuali.common.util.project.spring.AutowiredProjectConfig;
020import org.kuali.common.util.spring.SpringUtils;
021import org.kuali.common.util.spring.env.EnvironmentService;
022import org.kuali.common.util.spring.service.SpringServiceConfig;
023import org.springframework.beans.factory.annotation.Autowired;
024import org.springframework.context.annotation.Bean;
025import org.springframework.context.annotation.Configuration;
026import org.springframework.context.annotation.Import;
027
028/**
029 * TODO Should not be a class called {@code RiceSqlConfig} down here in kuali-util. Create a rice-util and move this there? Main issue preventing this from living in the rice-xml
030 * module itself is that it gets tricky having software used very early in the build lifecycle reside in the same project that makes use of it.
031 */
032@Configuration
033@Import({ AutowiredProjectConfig.class, MetaInfExecutableConfig.class, SpringServiceConfig.class })
034public class RiceSqlConfig implements MetaInfContextsConfig {
035
036        private static final boolean DEFAULT_GENERATE_RELATIVE_PATHS = true;
037        private static final String RELATIVE_KEY = MetaInfUtils.PROPERTY_PREFIX + ".sql.relative";
038        private static final String PREFIX = "sql";
039        private static final String DEFAULT_VENDORS = "mysql,oracle";
040        private static final String VENDORS_KEY = MetaInfUtils.PROPERTY_PREFIX + ".db.vendors";
041        private static final String IMPEX_ARTIFACT_ID = "rice-impex-master";
042
043        @Autowired
044        EnvironmentService env;
045
046        @Autowired
047        Project project;
048
049        @Autowired
050        Build build;
051
052        @Override
053        @Bean
054        public List<MetaInfContext> metaInfContexts() {
055                List<String> vendors = SpringUtils.getNoneSensitiveListFromCSV(env, VENDORS_KEY, DEFAULT_VENDORS);
056                List<MetaInfContext> contexts = new ArrayList<MetaInfContext>();
057                for (String vendor : vendors) {
058                        for (MetaInfGroup group : MetaInfGroup.values()) {
059                                MetaInfContext context = getMetaInfContext(group, vendor);
060                                contexts.add(context);
061                        }
062                }
063                return contexts;
064        }
065
066        protected MetaInfContext getMetaInfContext(MetaInfGroup group, String vendor) {
067                Map<MetaInfGroup, String> defaultIncludes = getDefaultIncludes(project, IMPEX_ARTIFACT_ID, vendor);
068                Map<MetaInfGroup, String> defaultExcludes = getDefaultExcludes(defaultIncludes);
069                boolean relativePaths = env.getBoolean(RELATIVE_KEY, DEFAULT_GENERATE_RELATIVE_PATHS);
070                File outputFile = MetaInfUtils.getOutputFile(project, build, vendor, group);
071                String includesKey = MetaInfConfigUtils.getIncludesKey(group, PREFIX) + "." + vendor;
072                String excludesKey = MetaInfConfigUtils.getExcludesKey(group, PREFIX) + "." + vendor;
073                List<String> includes = SpringUtils.getNoneSensitiveListFromCSV(env, includesKey, defaultIncludes.get(group));
074                List<String> excludes = SpringUtils.getNoneSensitiveListFromCSV(env, excludesKey, defaultExcludes.get(group));
075                File scanDir = build.getOutputDir();
076                String encoding = build.getEncoding();
077                Comparator<MetaInfResource> comparator = getComparator(group);
078                return new MetaInfContext.Builder(outputFile, encoding, scanDir).comparator(comparator).includes(includes).excludes(excludes).relativePaths(relativePaths).build();
079        }
080
081        protected Comparator<MetaInfResource> getComparator(MetaInfGroup group) {
082                if (MetaInfGroup.OTHER.equals(group)) {
083                        // The upgrades folder for Rice has a nested directory structure - [server|client]:[bootstrap|demo|test].
084                        // At the moment, the sorting of SQL located inside the "upgrades" folder for Rice ignores the directory structure and just sorts by filename.
085                        // The idea is that the "initial-db" folder inside Rice will soon have a structure similar to the "upgrades" folder.
086                        // This should enable "additive" dataset generation instead of "subtractive".
087                        // Once the "initial-db" structure is in place, this specialized comparator should be removed.
088                        // All SQL resources would then be sorted by the fully qualified location.
089                        return new MetaInfResourceFilenameComparator();
090                } else {
091                        return new MetaInfResourceLocationComparator();
092                }
093        }
094
095        protected Map<MetaInfGroup, String> getDefaultIncludes(Project project, String impexArtifactId, String vendor) {
096                String resourcePath = ProjectUtils.getResourcePath(project.getGroupId(), project.getArtifactId());
097                Map<MetaInfGroup, String> map = new HashMap<MetaInfGroup, String>();
098                map.put(MetaInfGroup.SCHEMA, resourcePath + "/initial-db/sql/" + vendor + "/" + impexArtifactId + ".sql");
099                map.put(MetaInfGroup.DATA, resourcePath + "/initial-db/sql/" + vendor + "/*.sql");
100                map.put(MetaInfGroup.CONSTRAINTS, resourcePath + "/initial-db/sql/" + vendor + "/" + impexArtifactId + "-constraints.sql");
101                map.put(MetaInfGroup.OTHER, resourcePath + "/upgrades/**/" + vendor + "/**/*.sql");
102                return map;
103        }
104
105        protected Map<MetaInfGroup, String> getDefaultExcludes(Map<MetaInfGroup, String> defaultIncludes) {
106                Map<MetaInfGroup, String> map = new HashMap<MetaInfGroup, String>();
107                // The schema includes is specific to one exact file, no need to exclude anything
108                map.put(MetaInfGroup.SCHEMA, NullUtils.NONE);
109                // Exclude the schema + constraints SQL
110                map.put(MetaInfGroup.DATA, defaultIncludes.get(MetaInfGroup.SCHEMA) + "," + defaultIncludes.get(MetaInfGroup.CONSTRAINTS));
111                // The constraint includes is specific to one exact file, no need to exclude anything
112                map.put(MetaInfGroup.CONSTRAINTS, NullUtils.NONE);
113                // No need to exclude any of the "upgrades" SQL
114                map.put(MetaInfGroup.OTHER, NullUtils.NONE);
115                return map;
116        }
117
118}