package org.solrmarc.index.indexer; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.marc4j.marc.Record; import org.solrmarc.index.collector.MultiValueCollector; import org.solrmarc.index.extractor.AbstractMultiValueExtractor; import org.solrmarc.index.extractor.AbstractSingleValueExtractor; import org.solrmarc.index.extractor.AbstractValueExtractor; import org.solrmarc.index.extractor.AbstractValueExtractorFactory; import org.solrmarc.index.extractor.ExternalMethod; import org.solrmarc.index.extractor.formatter.FieldFormatter.eCleanVal; import org.solrmarc.index.extractor.formatter.FieldFormatter.eJoinVal; import org.solrmarc.index.extractor.impl.direct.DirectMultiValueExtractor; import org.solrmarc.index.extractor.impl.java.JavaValueExtractorUtils; import org.solrmarc.index.extractor.methodcall.MethodCallManager; import org.solrmarc.index.extractor.methodcall.StaticMarcTestRecords; import org.solrmarc.index.mapping.AbstractMultiValueMapping; import org.solrmarc.index.mapping.AbstractValueMappingFactory; import org.solrmarc.index.utils.FastClasspathUtils; //import org.solrmarc.index.utils.ReflectionUtils; import org.solrmarc.tools.Utils; import bsh.TargetError; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ValueIndexerFactory { private final static Logger logger = Logger.getLogger(ValueIndexerFactory.class); private List<AbstractValueExtractorFactory> extractorFactories; private List<AbstractValueMappingFactory> mappingFactories; private List<IndexerSpecException> validationExceptions; private ThreadLocal<List<IndexerSpecException>> perRecordExceptions; private FullConditionalParser parser = null; private Properties localMappingProperties = null; private JavaValueExtractorUtils compileTool = null; private String homeDirStrs[] = null; private final Pattern specPattern = Pattern.compile("([-A-Za-z_0-9, \\t]*)([:=]|([+]=))(.*)"); boolean debug_parse = true; private boolean defaultUniqueVal = true; private final Pattern defaultUniquePattern = Pattern.compile("default.unique[ ]*[;=][ ]*[\"]?(true|false)[\"]?"); /** * The next three functions make the ValueIndexerFactory implement the * Singleton pattern To create of use a Factory do: * ValueIndexerFactory.instance() */ private static ValueIndexerFactory theFactory = null; public static ValueIndexerFactory initialize(String homeDirStrs[]) { if (homeDirStrs == null) { homeDirStrs = new String[] { "." }; } if (theFactory != null && Arrays.equals(homeDirStrs, theFactory.homeDirStrs)) return(theFactory); theFactory = new ValueIndexerFactory(homeDirStrs); try { theFactory.extractorFactories = theFactory.createExtractorFactories(FastClasspathUtils.getExtractorFactoryClasses()); theFactory.mappingFactories = theFactory.createMappingFactories(FastClasspathUtils.getMappingFactoryClasses()); } catch (IllegalAccessException | InstantiationException e) { throw new IndexerSpecException(e, "Error creating extractor or mapping factories"); } return (theFactory); } public static ValueIndexerFactory instance() { return (theFactory); } public ValueIndexerFactory(String homeDirStrs[]) { this.homeDirStrs = homeDirStrs; validationExceptions = new ArrayList<IndexerSpecException>(); // perRecordExceptions list changed to be a ThreadLocal list so that each thread can save // the exceptions found while indexing a given record without interfering with other indexing threads. perRecordExceptions = new ThreadLocal<List<IndexerSpecException>>() { @Override protected List<IndexerSpecException> initialValue() { return new ArrayList<>(); } }; List<String> dirsJavaSourceList = new ArrayList<String>(); String[] dirsJavaSource; for (String dirStr : homeDirStrs) { File dir = new File(dirStr); File dirIndexJava = new File(dirStr, "index_java"); File dirIndexJavaSrc = new File(dirIndexJava, "src"); if (dirIndexJava.exists() && dirIndexJavaSrc.exists()) { logger.info("Using directory: " + dirIndexJava.getAbsolutePath() + " as location of java sources"); dirsJavaSourceList.add(dir.getAbsolutePath()); } } dirsJavaSource = dirsJavaSourceList.toArray(new String[0]); compileTool = new JavaValueExtractorUtils(dirsJavaSource); compileTool.compileSources(); } public Class<?>[] getCompiledClasses() { return compileTool.getClasses(); } /** * Return ALL of the exceptions encountered while processing indexing specification * * @return */ public List<IndexerSpecException> getValidationExceptions() { return validationExceptions; } /** * Return ALL of the exceptions encountered while processing indexing specification * * @return */ public void addPerRecordError(IndexerSpecException error) { perRecordExceptions.get().add(error); } // /** // * Return the mapping factories loaded above for use in the CUP parser // * // * @return // */ // final private List<AbstractValueMappingFactory> getMappingFactories() // { // return mappingFactories; // } /** * Return the extractor factories loaded above for use in the CUP parser * * @return */ final List<AbstractValueExtractorFactory> getExtractorFactories() { return extractorFactories; } /** * Return ALL of the exceptions encountered while processing indexing specification * * @return */ public List<IndexerSpecException> getPerRecordErrors() { return perRecordExceptions.get(); } public void clearPerRecordErrors() { perRecordExceptions.get().clear(); } public Properties getLocalMappingProperties() { return localMappingProperties; } // private boolean isDebugParse() // { // return debug_parse; // } // // private void setDebugParse(boolean debug_parse) // { // this.debug_parse = debug_parse; // } // public List<AbstractValueIndexer<?>> createValueIndexers(File indexSpecFiles[]) throws IllegalAccessException, InstantiationException, IOException { validationExceptions.clear(); localMappingProperties = new Properties(); Map<String, List<AbstractValueIndexer<?>>> valueIndexerMap = new LinkedHashMap<>(); createValueIndexers(indexSpecFiles, valueIndexerMap); List<AbstractValueIndexer<?>> valueIndexers = collapseMapToList(valueIndexerMap); return valueIndexers; } // private List<AbstractValueIndexer<?>> createValueIndexers(File indexSpecFile) throws IllegalAccessException, InstantiationException, IOException // { // validationExceptions.clear(); // localMappingProperties = new Properties(); // // Map<String, List<AbstractValueIndexer<?>>> valueIndexerMap = new LinkedHashMap<>(); // // createValueIndexers(indexSpecFile, valueIndexerMap); // // List<AbstractValueIndexer<?>> valueIndexers = collapseMapToList(valueIndexerMap); // // return valueIndexers; // } public List<AbstractValueIndexer<?>> createValueIndexers(String configSpecs[]) throws IllegalAccessException, InstantiationException { validationExceptions.clear(); localMappingProperties = new Properties(); Map<String, List<AbstractValueIndexer<?>>> valueIndexerMap = new LinkedHashMap<>(); createValueIndexers(configSpecs, valueIndexerMap); List<AbstractValueIndexer<?>> valueIndexers = collapseMapToList(valueIndexerMap); return valueIndexers; } private List<AbstractValueIndexer<?>> collapseMapToList(Map<String, List<AbstractValueIndexer<?>>> valueIndexerMap) { List<AbstractValueIndexer<?>> valueIndexers = new ArrayList<>(); for (List<AbstractValueIndexer<?>> indexer : valueIndexerMap.values()) { valueIndexers.addAll(indexer); } return valueIndexers; } private void createValueIndexers(File indexSpecFiles[], Map<String, List<AbstractValueIndexer<?>>> valueIndexerMap) throws IllegalAccessException, InstantiationException, IOException { for (File indexSpecFile : indexSpecFiles) { createValueIndexers(indexSpecFile, valueIndexerMap); } } private void createValueIndexers(File indexSpecFile, Map<String, List<AbstractValueIndexer<?>>> valueIndexerMap) throws IllegalAccessException, InstantiationException, IOException { List<String> lines = new ArrayList<String>(); BufferedReader reader = new BufferedReader(new FileReader(indexSpecFile)); String line; String saveLine = ""; while ((line = reader.readLine()) != null) { if (saveLine.length() > 0 && line.matches("^[ \t].*")) { line = saveLine + line; saveLine = ""; } if (line.matches(".*,[ \t]*$")) { saveLine = line; } else { lines.add(line); } } if (saveLine.length() > 0) { lines.add(saveLine); } createValueIndexers(lines.toArray(new String[0]), valueIndexerMap); reader.close(); } private void createValueIndexers(String configSpecs[], Map<String, List<AbstractValueIndexer<?>>> valueIndexerMap) throws IllegalAccessException, InstantiationException { for (final String singleSpec : configSpecs) { if (singleSpec.startsWith("map.") || singleSpec.startsWith("pattern_map.")) { final String[] specParts = singleSpec.split("[ ]?[:=][ ]?", 2); specParts[1] = specParts[1].replaceAll("\\\\(.)", "$1"); localMappingProperties.put(specParts[0].trim(), specParts[1].trim()); } } for (String singleSpec : configSpecs) { singleSpec = singleSpec.trim(); if (singleSpec.startsWith("#") || (!singleSpec.contains(":") && !singleSpec.contains("="))) continue; if (singleSpec.startsWith("map.") || singleSpec.startsWith("pattern_map.")) continue; if (singleSpec.startsWith("default")) { if (singleSpec.startsWith("default.unique")) { Matcher defUnique = defaultUniquePattern.matcher(singleSpec); defUnique.matches(); this.defaultUniqueVal = Boolean.parseBoolean(defUnique.group(1)); } } Matcher match = specPattern.matcher(singleSpec); if (match.matches()) { final String solrFieldName = match.group(1).replaceAll("[ \\t]", ""); final String delimiter = match.group(2); final String mappingDefinition = match.group(4); try { MultiValueIndexer valueIndexer = createValueIndexer(solrFieldName, mappingDefinition); if (valueIndexer != null) { List<AbstractValueIndexer<?>> indexerList; if (delimiter.startsWith("+") && valueIndexerMap.containsKey(solrFieldName)) { indexerList = valueIndexerMap.get(solrFieldName); } else { indexerList = new ArrayList<>(); } indexerList.add(valueIndexer); valueIndexerMap.put(solrFieldName, indexerList); } for (IndexerSpecException ise : FullConditionalParser.getErrors()) { ise.setSolrFieldAndSpec(solrFieldName, mappingDefinition); validationExceptions.add(ise); } } catch (IndexerSpecException ise) { ise.setSolrFieldAndSpec(solrFieldName, mappingDefinition); validationExceptions.add(ise); } } } } public MultiValueIndexer createValueIndexer(String fieldNames, String indexSpec) { if (parser == null) { try { parser = new FullConditionalParser(debug_parse); parser.setFactories(this); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } } logger.trace("Processing spec: " + indexSpec); MultiValueIndexer valueIndexer = parser.parse(fieldNames, indexSpec); // Test fire the indexer to catch obvious error such as missing property // files if (valueIndexer != null) { boolean testFileMethod = Boolean.parseBoolean(System.getProperty("solrmarc.indexer.test.fire.method", "false")); if (testFileMethod) { try { logger.trace("Test firing spec: " + indexSpec); valueIndexer.getFieldData(StaticMarcTestRecords.testRecord[0]); } catch (InvocationTargetException ite) { throw new IndexerSpecException(ite.getTargetException(), "Error on test invocation of custom method: " + indexSpec); } catch (TargetError e) { throw new IndexerSpecException(e.getTarget(), "Error on test invocation of custom method: " + indexSpec); } catch (Exception e) { throw new IndexerSpecException(e, "Error on test invocation of custom method: " + indexSpec); } } } return (valueIndexer); } public static AbstractValueIndexer<?> makeThreadSafeCopy(AbstractValueIndexer<?> toClone) { String solrFieldNamesStr = toClone.getSolrFieldNamesStr(); String specLabel = toClone.getSpecLabel(); AbstractMultiValueExtractor extractor; MultiValueCollector collector = toClone.collector; AtomicLong totalElapsedTime = toClone.totalElapsedTime; AbstractMultiValueMapping[] mappings = new AbstractMultiValueMapping[toClone.mappings.length]; for (int i = 0; i < toClone.mappings.length; i++) { if (toClone.mappings[i] instanceof ExternalMethod && !((ExternalMethod) toClone.mappings[i]).isThreadSafe()) { mappings[i] = (AbstractMultiValueMapping) ((ExternalMethod) toClone.mappings[i]).makeThreadSafeCopy(); } else { mappings[i] = (AbstractMultiValueMapping) toClone.mappings[i]; } } if (toClone.extractor instanceof ExternalMethod && !((ExternalMethod) toClone.extractor).isThreadSafe()) { extractor = (AbstractMultiValueExtractor) ((ExternalMethod) toClone.extractor).makeThreadSafeCopy(); } else { extractor = (AbstractMultiValueExtractor) toClone.extractor; } MultiValueIndexer result = new MultiValueIndexer(solrFieldNamesStr, extractor, mappings, collector, specLabel, totalElapsedTime); return (result); } private List<AbstractValueExtractorFactory> createExtractorFactories(final Set<Class<? extends AbstractValueExtractorFactory>> factoryClasses) throws IllegalAccessException, InstantiationException { final List<AbstractValueExtractorFactory> factories = new ArrayList<>(factoryClasses.size()); for (final Class<? extends AbstractValueExtractorFactory> extractorFactoryClass : factoryClasses) { if (Modifier.isAbstract(extractorFactoryClass.getModifiers())) { continue; } logger.trace("Create value extractor factory for " + extractorFactoryClass); final AbstractValueExtractorFactory factory = extractorFactoryClass.newInstance(); factories.add(factory); } return factories; } private List<AbstractValueMappingFactory> createMappingFactories(final Set<Class<? extends AbstractValueMappingFactory>> factoryClasses) throws IllegalAccessException, InstantiationException { final List<AbstractValueMappingFactory> factories = new ArrayList<>(factoryClasses.size()); for (final Class<? extends AbstractValueMappingFactory> extractorFactoryClass : factoryClasses) { logger.trace("Create value mapping factory for s " + extractorFactoryClass); final AbstractValueMappingFactory factory = extractorFactoryClass.newInstance(); factories.add(factory); } return factories; } /** * Creates an indexer representing the indexer process for one (or more) * solr field (given by fieldnames), Where the extractor has already been * created in the parser handling code, and the maps definitions following * the extractor have been pulled out as a list of lists of strings. * * @param origSpec * the original index specification (for debugging purposes). * @param solrFieldName * the name (or names) of the solr field(s) this spec should * generate. * @param extractor * the extractor object created by the parser handling code (or * perhaps one manually created) * @param mapSpecs * the mapping/collector specifications definition for this * indexer. * @return an indexer representing the indexer process for one solr field. */ AbstractValueIndexer<?> makeMultiValueIndexer(String origSpec, String fieldnames, AbstractValueExtractor<?> extractor, List<List<String>> mapSpecs) { if (mapSpecs == null) { mapSpecs = new ArrayList<List<String>>(); } if (extractor instanceof DirectMultiValueExtractor) { final DirectMultiValueExtractor multiValueExtractor = (DirectMultiValueExtractor) extractor; int indexOfJoin = decorateMultiValueExtractor(origSpec, fieldnames, multiValueExtractor, mapSpecs); final List<AbstractMultiValueMapping> mappings; if (indexOfJoin != -1) mappings = createMultiValueMappings(origSpec, mapSpecs, indexOfJoin); else mappings = new ArrayList<AbstractMultiValueMapping>(); final MultiValueCollector collector = createMultiValueCollector(mapSpecs, true); return new MultiValueIndexer(fieldnames, multiValueExtractor, mappings, collector); } else if (extractor instanceof AbstractMultiValueExtractor) { final AbstractMultiValueExtractor multiValueExtractor = (AbstractMultiValueExtractor) extractor; final List<AbstractMultiValueMapping> mappings = createMultiValueMappings(origSpec, mapSpecs); final MultiValueCollector collector = createMultiValueCollector(mapSpecs); return new MultiValueIndexer(fieldnames, multiValueExtractor, mappings, collector); } else if (extractor instanceof AbstractSingleValueExtractor) { final AbstractSingleValueExtractor singleValueExtractor = (AbstractSingleValueExtractor) extractor; final List<AbstractMultiValueMapping> mappings = createMultiValueMappings(origSpec, mapSpecs); final MultiValueCollector collector = createMultiValueCollector(mapSpecs); return new MultiValueIndexer(fieldnames, singleValueExtractor, mappings, collector); } else if (extractor == null) { throw new IllegalArgumentException("Extractor is null, most likely there was an error parsing the index specification: " + origSpec); } else { throw new IllegalArgumentException("Only subclasses of AbstractMultiValueExtractor or AbstractSingleValueExtractor are allowed, but not " + extractor.getClass().getName()); } } boolean isADecoratorConfiguration(String str) { if (str.equals("join") || str.equals("separate") || str.equals("format") || str.equals("substring") || str.equals("cleanEach") || str.equals("cleanEnd") || str.equals("clean") || str.equals("stripAccent") || str.equals("stripPunct") || str.equals("stripInd2") || str.equals("toUpper") || str.equals("toLower") || str.equals("toUpper") || str.equals("toLower") || str.equals("titleSortUpper") || str.equals("titleSortLower") || str.equals("untrimmed") || str.equals("toTitleCase")) return (true); return (false); } private int decorateMultiValueExtractor(String origSpec, String fieldnames, DirectMultiValueExtractor multiValueExtractor, List<List<String>> mapSpecs) { if (mapSpecs.size() == 0) { return -1; } int currentIndex = 0; int joinIndex = -1; for (List<String> mapSpec : mapSpecs) { String mapParts[] = mapSpec.toArray(new String[0]); if (isACollectorConfiguration(mapParts[0])) { /* ignore, handle it elsewhere */ } else if (mapParts[0].equals("join")) { multiValueExtractor.setJoinVal(eJoinVal.JOIN); if (mapParts.length > 1) { multiValueExtractor.setSeparator(mapParts[1]); } joinIndex = currentIndex; } else if (mapParts[0].equals("separate")) { multiValueExtractor.setJoinVal(eJoinVal.SEPARATE); } else if (mapParts[0].equals("format")) { if (mapParts.length > 1) { multiValueExtractor.setFormatPatterns(mapParts); } } else if (mapParts[0].equals("substring")) { try { if (mapParts.length > 2) { multiValueExtractor.setSubstring(mapParts[1], mapParts[2]); } else { multiValueExtractor.setSubstring(mapParts[1], "toEnd"); } } catch (IndexerSpecException ise) { ise.setSolrFieldAndSpec(fieldnames, origSpec); validationExceptions.add(ise); } } else if (mapParts[0].equals("cleanEach")) { multiValueExtractor.addCleanVal(eCleanVal.CLEAN_EACH); } else if (mapParts[0].equals("untrimmed")) { multiValueExtractor.addCleanVal(eCleanVal.UNTRIMMED); } else if (mapParts[0].equals("cleanEnd")) { multiValueExtractor.addCleanVal(eCleanVal.CLEAN_END); } else if (mapParts[0].equals("clean")) { multiValueExtractor.addCleanVal(eCleanVal.CLEAN_EACH); multiValueExtractor.addCleanVal(eCleanVal.CLEAN_END); } else if (mapParts[0].equals("stripAccent")) { multiValueExtractor.addCleanVal(eCleanVal.STRIP_ACCCENTS); } else if (mapParts[0].equals("stripPunct")) { multiValueExtractor.addCleanVal(eCleanVal.STRIP_ALL_PUNCT); } else if (mapParts[0].equals("stripInd2")) { multiValueExtractor.addCleanVal(eCleanVal.STRIP_INDICATOR_2); } else if (mapParts[0].equals("toUpper")) { multiValueExtractor.addCleanVal(eCleanVal.TO_UPPER); } else if (mapParts[0].equals("toLower")) { multiValueExtractor.addCleanVal(eCleanVal.TO_LOWER); } else if (mapParts[0].equals("toTitleCase")) { multiValueExtractor.addCleanVal(eCleanVal.TO_TITLECASE); } else if (mapParts[0].equals("titleSortUpper")) { multiValueExtractor.setCleanVal(EnumSet.of(eCleanVal.CLEAN_EACH, eCleanVal.CLEAN_END, eCleanVal.STRIP_ACCCENTS, eCleanVal.STRIP_ALL_PUNCT, eCleanVal.STRIP_INDICATOR_2)); multiValueExtractor.addCleanVal(eCleanVal.TO_UPPER); } else if (mapParts[0].equals("titleSortLower")) { multiValueExtractor.setCleanVal(EnumSet.of(eCleanVal.CLEAN_EACH, eCleanVal.CLEAN_END, eCleanVal.STRIP_ACCCENTS, eCleanVal.STRIP_ALL_PUNCT, eCleanVal.STRIP_INDICATOR_2)); multiValueExtractor.addCleanVal(eCleanVal.TO_LOWER); } else if (isAValueMappingConfiguration(mapParts[0]) && joinIndex == -1) { AbstractMultiValueMapping valueMapping = createMultiValueMapping(mapParts); multiValueExtractor.addMap(valueMapping); } else if (isAValueMappingConfiguration(mapParts[0]) && joinIndex != -1) { // post join map specification // mapping specs before "join" are applied before the join operation // mapping spec that are after "join" are applied to the joined output. // and are handled elsewhere. } else { validationExceptions.add(new IndexerSpecException(fieldnames, origSpec, "Illegal format specification: " + Utils.join(mapParts, " "))); } currentIndex++; } return (joinIndex); } private boolean isAValueMappingConfiguration(final String configuration) { // if (configuration.matches(".+[.]properties([(][A-Za-z0-9]*[)])?") || configuration.matches("[(]this[)][.]properties([(][A-Za-z0-9]*[)])?") || // configuration.startsWith("map") || configuration.startsWith("filter") || configuration.startsWith("custom_map") || // configuration.matches("([a-z]+[.])*(map|filter)[A-Za-z0-9]+")) // { // return (true); // } for (final AbstractValueMappingFactory mappingFactory : mappingFactories) { if (mappingFactory.canHandle(configuration)) { return true; } } return (false); } private List<AbstractMultiValueMapping> createMultiValueMappings(String origSpec, List<List<String>> mapSpecs) { return (createMultiValueMappings(origSpec, mapSpecs, -1)); } /** * Creates the multivalue mappers as specified to be appended to a * extractor. (given by fieldnames), Where the extractor has already been * created in the parser handling code, and the maps definitions following * the extractor have been pulled out as a list of lists of strings. * * @param origSpec * the original index specification (for debugging purposes). * @param mapSpecs * the mapping/collector specifications definition for this * indexer. * @param indexOfJoin * index within mapSpecs where the "join" specification occurs. * mapping specs before "join" are applied before the join * operation mapping spec that are after "join" are applied to * the joined output. * @return an list of multi value mappings */ private List<AbstractMultiValueMapping> createMultiValueMappings(String origSpec, List<List<String>> mapSpecs, int indexOfJoin) { List<AbstractMultiValueMapping> maps = new ArrayList<AbstractMultiValueMapping>(mapSpecs.size()); if (mapSpecs.size() == 0) { return maps; } int currentIndex = 0; for (List<String> mapSpec : mapSpecs) { if (currentIndex > indexOfJoin) { String mapParts[] = mapSpec.toArray(new String[0]); if (isACollectorConfiguration(mapParts[0])) { /* ignore */ } else if (isADecoratorConfiguration(mapParts[0])) { /* ignore */ } else if (isAValueMappingConfiguration(mapParts[0])) { AbstractMultiValueMapping valueMapping = createMultiValueMapping(mapParts); if (valueMapping != null) maps.add(valueMapping); } else { validationExceptions.add(new IndexerSpecException(origSpec, "Illegal format specification: " + Utils.join(mapParts, " "))); } } currentIndex++; } return maps; } private boolean isACollectorConfiguration(String string) { if (string.equals("unique") || string.equals("first") || string.equals("sort") || string.equals("notunique") || string.equals("notfirst") || string.equals("all") || string.equals("DeleteRecordIfFieldEmpty")) return (true); return (false); } // private MultiValueCollector createMultiValueCollector() // { // MultiValueCollector collector = new MultiValueCollector(); // collector.setUnique(this.defaultUniqueVal); // return (collector); // } private MultiValueCollector createMultiValueCollector(List<List<String>> mapSpecs, boolean setDefaultValForUnique) { MultiValueCollector collector = new MultiValueCollector(); if (setDefaultValForUnique) collector.setUnique(this.defaultUniqueVal); for (List<String> mapSpec : mapSpecs) { String mapParts[] = mapSpec.toArray(new String[0]); if (isACollectorConfiguration(mapParts[0])) { if (mapParts[0].equals("unique")) { collector.setUnique(true); } else if (mapParts[0].equals("notunique")) { collector.setUnique(false); } else if (mapParts[0].equals("first")) { collector.setFirst(mapParts[0]); } else if (mapParts[0].equals("notfirst")) { collector.setFirst(mapParts[0]); } else if (mapParts[0].equals("all")) { collector.setFirst(mapParts[0]); } else if (mapParts[0].equals("sort")) { collector.setSortComparator(mapParts[1], mapParts[2]); } else if (mapParts[0].equals("DeleteRecordIfFieldEmpty")) { collector.setDeleteRecordIfEmpty(); } } } return collector; } private MultiValueCollector createMultiValueCollector(List<List<String>> mapSpecs) { return (createMultiValueCollector(mapSpecs, false)); } // currently used by SolrIndexer methods that provide backwards-compatibility public AbstractMultiValueMapping createMultiValueMapping(final String mappingConfig) { for (final AbstractValueMappingFactory mappingFactory : mappingFactories) { if (mappingFactory.canHandle(mappingConfig)) { return mappingFactory.createMultiValueMapping(mappingConfig); } } throw new IndexerSpecException("Could not handle impl: " + mappingConfig + "\nLoaded impl factories:\n" + mappingFactories.toString().replaceAll(",", ",\n")); } private AbstractMultiValueMapping createMultiValueMapping(String[] mapParts) { for (final AbstractValueMappingFactory mappingFactory : mappingFactories) { if (mappingFactory.canHandle(mapParts[0])) { return mappingFactory.createMultiValueMapping(mapParts); } } throw new IndexerSpecException("Could not handle map descriptor: " + Utils.join(mapParts, " ")); } public String[] getHomeDirs() { return (homeDirStrs); } public void doneWithRecord(Record record) { MethodCallManager.instance().doneWithRecord(record); } }