Author: kwright
Date: Thu Jun 19 00:35:36 2014
New Revision: 1603687
URL: http://svn.apache.org/r1603687
Log:
Add field mapping tab to the tika transformer. Part of CONNECTORS-954.
Added:
manifoldcf/trunk/connectors/tika/connector/src/main/java/org/apache/manifoldcf/agents/transformer/tika/TikaConfig.java
- copied unchanged from r1603686,
manifoldcf/branches/CONNECTORS-954/connectors/tika/connector/src/main/java/org/apache/manifoldcf/agents/transformer/tika/TikaConfig.java
manifoldcf/trunk/connectors/tika/connector/src/main/native2ascii/org/apache/manifoldcf/agents/transformation/
- copied from r1603686,
manifoldcf/branches/CONNECTORS-954/connectors/tika/connector/src/main/native2ascii/org/apache/manifoldcf/agents/transformation/
manifoldcf/trunk/connectors/tika/connector/src/main/resources/
- copied from r1603686,
manifoldcf/branches/CONNECTORS-954/connectors/tika/connector/src/main/resources/
Removed:
manifoldcf/trunk/connectors/tika/connector/src/main/native2ascii/org/apache/manifoldcf/agents/transformer/
Modified:
manifoldcf/trunk/ (props changed)
manifoldcf/trunk/connectors/tika/connector/src/main/java/org/apache/manifoldcf/agents/transformer/tika/TikaExtractor.java
Propchange: manifoldcf/trunk/
------------------------------------------------------------------------------
Merged /manifoldcf/branches/CONNECTORS-954:r1603652-1603686
Modified:
manifoldcf/trunk/connectors/tika/connector/src/main/java/org/apache/manifoldcf/agents/transformer/tika/TikaExtractor.java
URL:
http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/tika/connector/src/main/java/org/apache/manifoldcf/agents/transformer/tika/TikaExtractor.java?rev=1603687&r1=1603686&r2=1603687&view=diff
==============================================================================
---
manifoldcf/trunk/connectors/tika/connector/src/main/java/org/apache/manifoldcf/agents/transformer/tika/TikaExtractor.java
(original)
+++
manifoldcf/trunk/connectors/tika/connector/src/main/java/org/apache/manifoldcf/agents/transformer/tika/TikaExtractor.java
Thu Jun 19 00:35:36 2014
@@ -41,6 +41,10 @@ public class TikaExtractor extends org.a
{
public static final String _rcsid = "@(#)$Id$";
+ private static final String EDIT_SPECIFICATION_JS = "editSpecification.js";
+ private static final String EDIT_SPECIFICATION_FIELDMAPPING_HTML =
"editSpecification_FieldMapping.html";
+ private static final String VIEW_SPECIFICATION_HTML =
"viewSpecification.html";
+
protected static final String ACTIVITY_EXTRACT = "extract";
protected static final String[] activitiesList = new
String[]{ACTIVITY_EXTRACT};
@@ -58,6 +62,25 @@ public class TikaExtractor extends org.a
return activitiesList;
}
+ /** Get an output version string, given an output specification. The output
version string is used to uniquely describe the pertinent details of
+ * the output specification and the configuration, to allow the Connector
Framework to determine whether a document will need to be output again.
+ * Note that the contents of the document cannot be considered by this
method, and that a different version string (defined in IRepositoryConnector)
+ * is used to describe the version of the actual document.
+ *
+ * This method presumes that the connector object has been configured, and it
is thus able to communicate with the output data store should that be
+ * necessary.
+ *@param os is the current output specification for the job that is doing the
crawling.
+ *@return a string, of unlimited length, which uniquely describes output
configuration and specification in such a way that if two such strings are
equal,
+ * the document will not need to be sent again to the output data store.
+ */
+ @Override
+ public String getPipelineDescription(Specification os)
+ throws ManifoldCFException, ServiceInterruption
+ {
+ SpecPacker sp = new SpecPacker(os);
+ return sp.toPackedString();
+ }
+
/** Add (or replace) a document in the output data store using the connector.
* This method presumes that the connector object has been configured, and it
is thus able to communicate with the output data store should that be
* necessary.
@@ -78,6 +101,8 @@ public class TikaExtractor extends org.a
public int addOrReplaceDocumentWithException(String documentURI, String
pipelineDescription, RepositoryDocument document, String authorityNameString,
IOutputAddActivity activities)
throws ManifoldCFException, ServiceInterruption, IOException
{
+ SpecPacker sp = new SpecPacker(pipelineDescription);
+
// Tika's API reads from an input stream and writes to an output Writer.
// Since a RepositoryDocument includes readers and inputstreams
exclusively, AND all downstream
// processing needs to occur in a ManifoldCF thread, we have some
constraints on the architecture we need to get this done:
@@ -180,7 +205,18 @@ public class TikaExtractor extends org.a
String[] metaNames = metadata.names();
for(String mName : metaNames){
String value = metadata.get(mName);
- docCopy.addField(mName,value);
+ String target = sp.getMapping(mName);
+ if(target!=null)
+ {
+ docCopy.addField(target, value);
+ }
+ else
+ {
+ if(sp.keepAllMetadata())
+ {
+ docCopy.addField(mName, value);
+ }
+ }
}
// Send new document downstream
@@ -201,6 +237,221 @@ public class TikaExtractor extends org.a
}
+ /** Obtain the name of the form check javascript method to call.
+ *@param connectionSequenceNumber is the unique number of this connection
within the job.
+ *@return the name of the form check javascript method.
+ */
+ @Override
+ public String getFormCheckJavascriptMethodName(int connectionSequenceNumber)
+ {
+ return "s"+connectionSequenceNumber+"_checkSpecification";
+ }
+
+ /** Obtain the name of the form presave check javascript method to call.
+ *@param connectionSequenceNumber is the unique number of this connection
within the job.
+ *@return the name of the form presave check javascript method.
+ */
+ @Override
+ public String getFormPresaveCheckJavascriptMethodName(int
connectionSequenceNumber)
+ {
+ return "s"+connectionSequenceNumber+"_checkSpecificationForSave";
+ }
+
+ /** Output the specification header section.
+ * This method is called in the head section of a job page which has selected
a pipeline connection of the current type. Its purpose is to add the required
tabs
+ * to the list, and to output any javascript methods that might be needed by
the job editing HTML.
+ *@param out is the output to which any HTML should be sent.
+ *@param locale is the preferred local of the output.
+ *@param os is the current pipeline specification for this connection.
+ *@param connectionSequenceNumber is the unique number of this connection
within the job.
+ *@param tabsArray is an array of tab names. Add to this array any tab names
that are specific to the connector.
+ */
+ @Override
+ public void outputSpecificationHeader(IHTTPOutput out, Locale locale,
Specification os,
+ int connectionSequenceNumber, List<String> tabsArray)
+ throws ManifoldCFException, IOException
+ {
+ Map<String, Object> paramMap = new HashMap<String, Object>();
+ paramMap.put("SEQNUM",Integer.toString(connectionSequenceNumber));
+
+ tabsArray.add(Messages.getString(locale,
"TikaExtractor.FieldMappingTabName"));
+
+ // Fill in the specification header map, using data from all tabs.
+ fillInFieldMappingSpecificationMap(paramMap, os);
+
+
Messages.outputResourceWithVelocity(out,locale,EDIT_SPECIFICATION_JS,paramMap);
+ }
+
+ /** Output the specification body section.
+ * This method is called in the body section of a job page which has selected
a pipeline connection of the current type. Its purpose is to present the
required form elements for editing.
+ * The coder can presume that the HTML that is output from this configuration
will be within appropriate <html>, <body>, and <form> tags. The name of the
+ * form is "editjob".
+ *@param out is the output to which any HTML should be sent.
+ *@param locale is the preferred local of the output.
+ *@param os is the current pipeline specification for this job.
+ *@param connectionSequenceNumber is the unique number of this connection
within the job.
+ *@param actualSequenceNumber is the connection within the job that has
currently been selected.
+ *@param tabName is the current tab name.
+ */
+ @Override
+ public void outputSpecificationBody(IHTTPOutput out, Locale locale,
Specification os,
+ int connectionSequenceNumber, int actualSequenceNumber, String tabName)
+ throws ManifoldCFException, IOException
+ {
+ Map<String, Object> paramMap = new HashMap<String, Object>();
+
+ // Set the tab name
+ paramMap.put("TABNAME", tabName);
+ paramMap.put("SEQNUM",Integer.toString(connectionSequenceNumber));
+ paramMap.put("SELECTEDNUM",Integer.toString(actualSequenceNumber));
+
+ // Fill in the field mapping tab data
+ fillInFieldMappingSpecificationMap(paramMap, os);
+
Messages.outputResourceWithVelocity(out,locale,EDIT_SPECIFICATION_FIELDMAPPING_HTML,paramMap);
+ }
+
+ /** Process a specification post.
+ * This method is called at the start of job's edit or view page, whenever
there is a possibility that form data for a connection has been
+ * posted. Its purpose is to gather form information and modify the
transformation specification accordingly.
+ * The name of the posted form is "editjob".
+ *@param variableContext contains the post data, including binary file-upload
information.
+ *@param locale is the preferred local of the output.
+ *@param os is the current pipeline specification for this job.
+ *@param connectionSequenceNumber is the unique number of this connection
within the job.
+ *@return null if all is well, or a string error message if there is an error
that should prevent saving of the job (and cause a redirection to an error
page).
+ */
+ @Override
+ public String processSpecificationPost(IPostParameters variableContext,
Locale locale, Specification os,
+ int connectionSequenceNumber)
+ throws ManifoldCFException {
+ String seqPrefix = "s"+connectionSequenceNumber+"_";
+
+ String x;
+
+ x = variableContext.getParameter(seqPrefix+"fieldmapping_count");
+ if (x != null && x.length() > 0)
+ {
+ // About to gather the fieldmapping nodes, so get rid of the old ones.
+ int i = 0;
+ while (i < os.getChildCount())
+ {
+ SpecificationNode node = os.getChild(i);
+ if (node.getType().equals(TikaConfig.NODE_FIELDMAP) ||
node.getType().equals(TikaConfig.NODE_KEEPMETADATA))
+ os.removeChild(i);
+ else
+ i++;
+ }
+ int count = Integer.parseInt(x);
+ i = 0;
+ while (i < count)
+ {
+ String prefix = seqPrefix+"fieldmapping_";
+ String suffix = "_"+Integer.toString(i);
+ String op = variableContext.getParameter(prefix+"op"+suffix);
+ if (op == null || !op.equals("Delete"))
+ {
+ // Gather the fieldmap etc.
+ String source = variableContext.getParameter(prefix+"source"+suffix);
+ String target = variableContext.getParameter(prefix+"target"+suffix);
+ if (target == null)
+ target = "";
+ SpecificationNode node = new
SpecificationNode(TikaConfig.NODE_FIELDMAP);
+ node.setAttribute(TikaConfig.ATTRIBUTE_SOURCE,source);
+ node.setAttribute(TikaConfig.ATTRIBUTE_TARGET,target);
+ os.addChild(os.getChildCount(),node);
+ }
+ i++;
+ }
+
+ String addop = variableContext.getParameter(seqPrefix+"fieldmapping_op");
+ if (addop != null && addop.equals("Add"))
+ {
+ String source =
variableContext.getParameter(seqPrefix+"fieldmapping_source");
+ String target =
variableContext.getParameter(seqPrefix+"fieldmapping_target");
+ if (target == null)
+ target = "";
+ SpecificationNode node = new
SpecificationNode(TikaConfig.NODE_FIELDMAP);
+ node.setAttribute(TikaConfig.ATTRIBUTE_SOURCE,source);
+ node.setAttribute(TikaConfig.ATTRIBUTE_TARGET,target);
+ os.addChild(os.getChildCount(),node);
+ }
+
+ // Gather the keep all metadata parameter to be the last one
+ SpecificationNode node = new
SpecificationNode(TikaConfig.NODE_KEEPMETADATA);
+ String keepAll =
variableContext.getParameter(seqPrefix+"keepallmetadata");
+ if (keepAll != null)
+ {
+ node.setAttribute(TikaConfig.ATTRIBUTE_VALUE, keepAll);
+ }
+ else
+ {
+ node.setAttribute(TikaConfig.ATTRIBUTE_VALUE, "false");
+ }
+ // Add the new keepallmetadata config parameter
+ os.addChild(os.getChildCount(), node);
+ }
+
+ return null;
+ }
+
+
+ /** View specification.
+ * This method is called in the body section of a job's view page. Its
purpose is to present the pipeline specification information to the user.
+ * The coder can presume that the HTML that is output from this configuration
will be within appropriate <html> and <body> tags.
+ *@param out is the output to which any HTML should be sent.
+ *@param locale is the preferred local of the output.
+ *@param connectionSequenceNumber is the unique number of this connection
within the job.
+ *@param os is the current pipeline specification for this job.
+ */
+ @Override
+ public void viewSpecification(IHTTPOutput out, Locale locale, Specification
os,
+ int connectionSequenceNumber)
+ throws ManifoldCFException, IOException
+ {
+ Map<String, Object> paramMap = new HashMap<String, Object>();
+ paramMap.put("SEQNUM",Integer.toString(connectionSequenceNumber));
+
+ // Fill in the map with data from all tabs
+ fillInFieldMappingSpecificationMap(paramMap, os);
+
+
Messages.outputResourceWithVelocity(out,locale,VIEW_SPECIFICATION_HTML,paramMap);
+
+ }
+
+ protected static void fillInFieldMappingSpecificationMap(Map<String,Object>
paramMap, Specification os)
+ {
+ // Prep for field mappings
+ List<Map<String,String>> fieldMappings = new
ArrayList<Map<String,String>>();
+ String keepAllMetadataValue = "true";
+ for (int i = 0; i < os.getChildCount(); i++)
+ {
+ SpecificationNode sn = os.getChild(i);
+ if (sn.getType().equals(TikaConfig.NODE_FIELDMAP)) {
+ String source = sn.getAttributeValue(TikaConfig.ATTRIBUTE_SOURCE);
+ String target = sn.getAttributeValue(TikaConfig.ATTRIBUTE_TARGET);
+ String targetDisplay;
+ if (target == null)
+ {
+ target = "";
+ targetDisplay = "(remove)";
+ }
+ else
+ targetDisplay = target;
+ Map<String,String> fieldMapping = new HashMap<String,String>();
+ fieldMapping.put("SOURCE",source);
+ fieldMapping.put("TARGET",target);
+ fieldMapping.put("TARGETDISPLAY",targetDisplay);
+ fieldMappings.add(fieldMapping);
+ }
+ else if (sn.getType().equals(TikaConfig.NODE_KEEPMETADATA))
+ {
+ keepAllMetadataValue =
sn.getAttributeValue(TikaConfig.ATTRIBUTE_VALUE);
+ }
+ }
+ paramMap.put("FIELDMAPPINGS",fieldMappings);
+ paramMap.put("KEEPALLMETADATA",keepAllMetadataValue);
+ }
+
protected static int handleTikaException(TikaException e)
throws IOException, ManifoldCFException, ServiceInterruption
{
@@ -363,7 +614,96 @@ public class TikaExtractor extends org.a
}
}
-
+
+ protected static class SpecPacker {
+
+ private final Map<String,String> sourceTargets = new
HashMap<String,String>();
+ private final boolean keepAllMetadata;
+
+ public SpecPacker(Specification os) {
+ boolean keepAllMetadata = true;
+ for (int i = 0; i < os.getChildCount(); i++) {
+ SpecificationNode sn = os.getChild(i);
+
+ if(sn.getType().equals(TikaConfig.NODE_KEEPMETADATA)) {
+ String value = sn.getAttributeValue(TikaConfig.ATTRIBUTE_VALUE);
+ keepAllMetadata = Boolean.parseBoolean(value);
+ } else if (sn.getType().equals(TikaConfig.NODE_FIELDMAP)) {
+ String source = sn.getAttributeValue(TikaConfig.ATTRIBUTE_SOURCE);
+ String target = sn.getAttributeValue(TikaConfig.ATTRIBUTE_TARGET);
+
+ if (target == null) {
+ target = "";
+ }
+ sourceTargets.put(source, target);
+ }
+ }
+ this.keepAllMetadata = keepAllMetadata;
+ }
+
+ public SpecPacker(String packedString) {
+
+ int index = 0;
+
+ // Mappings
+ final List<String> packedMappings = new ArrayList<String>();
+ index = unpackList(packedMappings,packedString,index,'+');
+ String[] fixedList = new String[2];
+ for (String packedMapping : packedMappings) {
+ unpackFixedList(fixedList,packedMapping,0,':');
+ sourceTargets.put(fixedList[0], fixedList[1]);
+ }
+
+ // Keep all metadata
+ if (packedString.length() > index)
+ keepAllMetadata = (packedString.charAt(index++) == '+');
+ else
+ keepAllMetadata = true;
+
+ }
+
+ public String toPackedString() {
+ StringBuilder sb = new StringBuilder();
+ int i;
+
+ // Mappings
+ final String[] sortArray = new String[sourceTargets.size()];
+ i = 0;
+ for (String source : sourceTargets.keySet()) {
+ sortArray[i++] = source;
+ }
+ java.util.Arrays.sort(sortArray);
+
+ List<String> packedMappings = new ArrayList<String>();
+ String[] fixedList = new String[2];
+ for (String source : sortArray) {
+ String target = sourceTargets.get(source);
+ StringBuilder localBuffer = new StringBuilder();
+ fixedList[0] = source;
+ fixedList[1] = target;
+ packFixedList(localBuffer,fixedList,':');
+ packedMappings.add(localBuffer.toString());
+ }
+ packList(sb,packedMappings,'+');
+
+ // Keep all metadata
+ if (keepAllMetadata)
+ sb.append('+');
+ else
+ sb.append('-');
+
+ return sb.toString();
+ }
+
+ public String getMapping(String source) {
+ return sourceTargets.get(source);
+ }
+
+ public boolean keepAllMetadata() {
+ return keepAllMetadata;
+ }
+ }
+
}