You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2011/07/18 23:40:20 UTC
svn commit: r1148064 - in /incubator/lcf/branches/CONNECTORS-221: ./
connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/
Author: kwright
Date: Mon Jul 18 21:40:19 2011
New Revision: 1148064
URL: http://svn.apache.org/viewvc?rev=1148064&view=rev
Log:
Add CMIS authority connector
Added:
incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisAuthorityConnector.java (with props)
incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/MatchMap.java (with props)
Modified:
incubator/lcf/branches/CONNECTORS-221/build.xml
Modified: incubator/lcf/branches/CONNECTORS-221/build.xml
URL: http://svn.apache.org/viewvc/incubator/lcf/branches/CONNECTORS-221/build.xml?rev=1148064&r1=1148063&r2=1148064&view=diff
==============================================================================
--- incubator/lcf/branches/CONNECTORS-221/build.xml (original)
+++ incubator/lcf/branches/CONNECTORS-221/build.xml Mon Jul 18 21:40:19 2011
@@ -1434,6 +1434,7 @@
<fileset dir="connectors/cmis/dist/lib"/>
</copy>
<replace file="dist/example/connectors.xml" token="<!-- Add your repository connectors here -->" value="<!-- Add your repository connectors here -->
 <repositoryconnector name="CMIS" class="org.apache.manifoldcf.crawler.connectors.cmis.CmisRepositoryConnector"/>"/>
+ <replace file="dist/example/connectors.xml" token="<!-- Add your authority connectors here -->" value="<!-- Add your authority connectors here -->
 <authorityconnector name="CMIS" class="org.apache.manifoldcf.crawler.connectors.cmis.CmisAuthorityConnector"/>"/>
</target>
<target name="deliver-documentum-example" depends="deliver-framework-example,calculate-documentum-condition" if="documentum.include">
Added: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisAuthorityConnector.java
URL: http://svn.apache.org/viewvc/incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisAuthorityConnector.java?rev=1148064&view=auto
==============================================================================
--- incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisAuthorityConnector.java (added)
+++ incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisAuthorityConnector.java Mon Jul 18 21:40:19 2011
@@ -0,0 +1,799 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.apache.manifoldcf.crawler.connectors.cmis;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.client.api.Session;
+import org.apache.chemistry.opencmis.client.api.SessionFactory;
+import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
+import org.apache.chemistry.opencmis.commons.SessionParameter;
+import org.apache.chemistry.opencmis.commons.enums.BindingType;
+import org.apache.commons.lang.StringUtils;
+import org.apache.manifoldcf.agents.interfaces.ServiceInterruption;
+import org.apache.manifoldcf.authorities.authorities.BaseAuthorityConnector;
+import org.apache.manifoldcf.authorities.interfaces.AuthorizationResponse;
+import org.apache.manifoldcf.core.interfaces.CacheManagerFactory;
+import org.apache.manifoldcf.core.interfaces.ConfigParams;
+import org.apache.manifoldcf.core.interfaces.ICacheCreateHandle;
+import org.apache.manifoldcf.core.interfaces.ICacheDescription;
+import org.apache.manifoldcf.core.interfaces.ICacheHandle;
+import org.apache.manifoldcf.core.interfaces.ICacheManager;
+import org.apache.manifoldcf.core.interfaces.IHTTPOutput;
+import org.apache.manifoldcf.core.interfaces.IPostParameters;
+import org.apache.manifoldcf.core.interfaces.IThreadContext;
+import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
+import org.apache.manifoldcf.core.interfaces.StringSet;
+import org.apache.manifoldcf.crawler.system.Logging;
+import org.apache.manifoldcf.ui.util.Encoder;
+
+/**
+ *
+ * The CMIS Authority Connector is based only on a regular expression checker, because the CMIS specification
+ * (and the Apache Chemistry implementation) doesn't have any exposed method to get info about users.
+ *
+ * For the configuration we can assume that we could have different users for any CMIS repository that
+ * could have the same endpoint. That's why for this connector is required the Repository ID.
+ *
+ * @author Piergiorgio Lucidi
+ *
+ */
+public class CmisAuthorityConnector extends BaseAuthorityConnector {
+
+ public static final String CONFIG_PARAM_USERNAME = "username";
+ public static final String CONFIG_PARAM_PASSWORD = "password";
+ public static final String CONFIG_PARAM_ENDPOINT = "endpoint";
+ public static final String CONFIG_PARAM_REPOSITORY_ID = "repositoryId";
+
+ /** User name mapping parameter */
+ protected static final String CONFIG_PARAM_USERMAPPING = "usermapping";
+
+ protected static final String CONFIG_PARAM_USERNAME_REGEXP = "usernameregexp";
+ protected static final String CONFIG_PARAM_USER_TRANSLATION = "usertranslation";
+
+ private static final String TAB_NAME_REPOSITORY = "Repository";
+ private static final String TAB_NAME_USER_MAPPING = "User Mapping";
+
+ private static final String DEFAULT_VALUE_USERNAME = "admin";
+ private static final String DEFAULT_VALUE_PASSWORD = "pwd";
+ private static final String DEFAULT_VALUE_ENDPOINT = "http://localhost:8080/cmis/";
+ private static final String DEFAULT_VALUE_REPOSITORY_ID = "uuid";
+
+
+ /**
+ * CMIS Session handle
+ */
+ Session session = null;
+
+ protected String username = null;
+ protected String password = null;
+ protected String endpoint = null;
+ protected String repositoryId = null;
+
+ protected SessionFactory factory = SessionFactoryImpl.newInstance();
+ protected Map<String, String> parameters = new HashMap<String, String>();
+
+ protected static final long timeToRelease = 300000L;
+ protected long lastSessionFetch = -1L;
+
+ /** The cache manager. */
+ protected ICacheManager cacheManager = null;
+
+ /** Match map for username mapping */
+ protected MatchMap matchMap = null;
+
+ protected static long responseLifetime = 60000L;
+ protected static int LRUsize = 1000;
+ protected static StringSet emptyStringSet = new StringSet();
+
+ /** This is the active directory global deny token. This should be ingested with all documents. */
+ public static final String GLOBAL_DENY_TOKEN = "DEAD_AUTHORITY";
+
+ /** Unreachable CMIS */
+ private static final AuthorizationResponse unreachableResponse = new AuthorizationResponse(
+ new String[]{GLOBAL_DENY_TOKEN},AuthorizationResponse.RESPONSE_UNREACHABLE);
+
+ /** User not found */
+ private static final AuthorizationResponse userNotFoundResponse = new AuthorizationResponse(
+ new String[]{GLOBAL_DENY_TOKEN},AuthorizationResponse.RESPONSE_USERNOTFOUND);
+
+ /** Set thread context.
+ */
+ @Override
+ public void setThreadContext(IThreadContext tc)
+ throws ManifoldCFException {
+ super.setThreadContext(tc);
+ cacheManager = CacheManagerFactory.make(tc);
+ }
+
+ /** Clear thread context.
+ */
+ @Override
+ public void clearThreadContext() {
+ super.clearThreadContext();
+ cacheManager = null;
+ }
+
+ public CmisAuthorityConnector() {
+ super();
+ }
+
+ protected class CheckConnectionThread extends Thread {
+ protected Throwable exception = null;
+
+ public CheckConnectionThread() {
+ super();
+ setDaemon(true);
+ }
+
+ public void run() {
+ try {
+ session.getRepositoryInfo();
+ } catch (Throwable e) {
+ this.exception = e;
+ }
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ }
+
+ protected class GetSessionThread extends Thread {
+ protected Throwable exception = null;
+
+ public GetSessionThread() {
+ super();
+ setDaemon(true);
+ }
+
+ public void run() {
+ try {
+ // Create a session
+ parameters.clear();
+
+ // user credentials
+ parameters.put(SessionParameter.USER, username);
+ parameters.put(SessionParameter.PASSWORD, password);
+
+ // connection settings
+ parameters.put(SessionParameter.ATOMPUB_URL, endpoint);
+ parameters.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
+
+ // get a session from the repository specified in the
+ // configuration with its own ID
+ parameters.put(SessionParameter.REPOSITORY_ID, repositoryId);
+ session = factory.createSession(parameters);
+
+ } catch (Throwable e) {
+ this.exception = e;
+ }
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+ }
+
+ protected class DestroySessionThread extends Thread {
+ protected Throwable exception = null;
+
+ public DestroySessionThread() {
+ super();
+ setDaemon(true);
+ }
+
+ public void run() {
+ try {
+ session = null;
+ } catch (Throwable e) {
+ this.exception = e;
+ }
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ }
+
+ /**
+ * This method create a new CMIS session for a CMIS repository, if the
+ * repositoryId is not provided in the configuration, the connector will
+ * retrieve all the repositories exposed for this endpoint the it will start
+ * to use the first one.
+ */
+ @Override
+ public void connect(ConfigParams configParams) {
+ super.connect(configParams);
+ username = params.getParameter(CONFIG_PARAM_USERNAME);
+ password = params.getParameter(CONFIG_PARAM_PASSWORD);
+ endpoint = params.getParameter(CONFIG_PARAM_ENDPOINT);
+ repositoryId = params.getParameter(CONFIG_PARAM_REPOSITORY_ID);
+ String userNameMapping = params.getParameter(CONFIG_PARAM_USERMAPPING);
+ matchMap = new MatchMap(userNameMapping);
+ }
+
+ /**
+ * Output the configuration body section. This method is called in the body
+ * section of the authority connector's configuration page. 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 "editconnection".
+ *
+ * @param threadContext
+ * is the local thread context.
+ * @param out
+ * is the output to which any HTML should be sent.
+ * @param parameters
+ * are the configuration parameters, as they currently exist, for
+ * this connection being configured.
+ * @param tabName
+ * is the current tab name.
+ */
+ @Override
+ public void outputConfigurationBody(IThreadContext threadContext,
+ IHTTPOutput out, ConfigParams parameters, String tabName)
+ throws ManifoldCFException, IOException {
+
+ String username = parameters.getParameter(CONFIG_PARAM_USERNAME);
+ String password = parameters.getParameter(CONFIG_PARAM_PASSWORD);
+ String endpoint = parameters.getParameter(CONFIG_PARAM_ENDPOINT);
+ String repositoryId = parameters.getParameter(CONFIG_PARAM_REPOSITORY_ID);
+
+ if(StringUtils.isEmpty(username))
+ username = DEFAULT_VALUE_USERNAME;
+ if(StringUtils.isEmpty(password))
+ password = DEFAULT_VALUE_PASSWORD;
+ if(StringUtils.isEmpty(endpoint))
+ endpoint = DEFAULT_VALUE_ENDPOINT;
+ if(StringUtils.isEmpty(repositoryId))
+ repositoryId = DEFAULT_VALUE_REPOSITORY_ID;
+
+ String userMappingString = parameters.getParameter(CONFIG_PARAM_USERMAPPING);
+ MatchMap localMap;
+ if (StringUtils.isNotEmpty(userMappingString)){
+ localMap = new MatchMap(userMappingString);
+ } else {
+ localMap = new MatchMap();
+ localMap.appendMatchPair("(.*)","$(1)");
+ }
+ String usernameRegexp = localMap.getMatchString(0);
+ String userTranslation = localMap.getReplaceString(0);
+
+ if (tabName.equals(TAB_NAME_REPOSITORY))
+ {
+ out.print("<table class=\"displaytable\">\n"
+ + " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n");
+ out.print(
+ "<tr><td class=\"description\"><nobr>Username:</nobr></td>\n"
+ +"<td class=\"value\"><input type=\"text\" name=\""
+ + CONFIG_PARAM_USERNAME + "\" value=\""+Encoder.attributeEscape(username)+"\"/></td></tr>\n");
+ out.print("<tr><td class=\"description\"><nobr>Password:</nobr></td>" +
+ "<td class=\"value\"><input type=\"password\" name=\""
+ + CONFIG_PARAM_PASSWORD + "\" value=\""+Encoder.attributeEscape(password)+"\"/></td></tr>\n");
+ out.print("<tr><td class=\"description\"><nobr>Endpoint:</nobr></td>" +
+ "<td class=\"value\"><input type=\"text\" name=\""
+ + CONFIG_PARAM_ENDPOINT + "\" value=\""+Encoder.attributeEscape(endpoint)+"\" size=\"50\"/></td></tr>\n");
+ out.print("<tr><td class=\"description\"><nobr>Repository ID:</nobr></td>" +
+ "<td class=\"value\"><input type=\"text\" name=\""
+ + CONFIG_PARAM_REPOSITORY_ID + "\" value=\""+Encoder.attributeEscape(repositoryId)+"\"/></td></tr>\n");
+ out.print("</table>\n");
+ }
+ else
+ {
+ out.print("<input type=\"hidden\" name=\""+CONFIG_PARAM_USERNAME+"\" value=\""+Encoder.attributeEscape(username)+"\"/>\n");
+ out.print("<input type=\"hidden\" name=\""+CONFIG_PARAM_PASSWORD+"\" value=\""+Encoder.attributeEscape(password)+"\"/>\n");
+ out.print("<input type=\"hidden\" name=\""+CONFIG_PARAM_ENDPOINT+"\" value=\""+Encoder.attributeEscape(endpoint)+"\"/>\n");
+ out.print("<input type=\"hidden\" name=\""+CONFIG_PARAM_REPOSITORY_ID+"\" value=\""+Encoder.attributeEscape(repositoryId)+"\"/>\n");
+ }
+
+ if (tabName.equals(TAB_NAME_USER_MAPPING))
+ {
+ out.print(
+"<table class=\"displaytable\">\n"+
+" <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
+" <tr>\n"+
+" <td class=\"description\"><nobr>User mapping:</nobr></td>\n"+
+" <td class=\"value\">\n"+
+" <input type=\"text\" size=\"32\" name=\""+CONFIG_PARAM_USERNAME_REGEXP+"\" value=\""+
+ Encoder.attributeEscape(usernameRegexp)+"\"/> ==> \n"+
+" <input type=\"text\" size=\"32\" name=\""+CONFIG_PARAM_USER_TRANSLATION+"\" value=\""+
+ Encoder.attributeEscape(userTranslation)+"\"/>\n"+
+" </td>\n"+
+" </tr>\n"+
+"</table>\n"
+ );
+ }
+ else
+ {
+ out.print(
+"<input type=\"hidden\" name=\"usernameregexp\" value=\""+
+ Encoder.attributeEscape(usernameRegexp)+"\"/>\n"+
+"<input type=\"hidden\" name=\"usertranslation\" value=\""+
+ Encoder.attributeEscape(userTranslation)+"\"/>\n"
+ );
+ }
+
+ }
+
+ /**
+ * Output the configuration header section. This method is called in the head
+ * section of the connector's configuration page. Its purpose is to add the
+ * required tabs to the list, and to output any javascript methods that might
+ * be needed by the configuration editing HTML.
+ *
+ * @param threadContext
+ * is the local thread context.
+ * @param out
+ * is the output to which any HTML should be sent.
+ * @param parameters
+ * are the configuration parameters, as they currently exist, for
+ * this connection being configured.
+ * @param tabsArray
+ * is an array of tab names. Add to this array any tab names that are
+ * specific to the connector.
+ */
+ @Override
+ public void outputConfigurationHeader(IThreadContext threadContext,
+ IHTTPOutput out, ConfigParams parameters, List<String> tabsArray)
+ throws ManifoldCFException, IOException {
+
+ tabsArray.add(TAB_NAME_REPOSITORY);
+ tabsArray.add(TAB_NAME_USER_MAPPING);
+
+ out.print("<script type=\"text/javascript\">\n" + "<!--\n"
+ + "function checkConfig()\n" + "{\n"
+ + " if (editconnection.username.value == \"\")\n" + " {\n"
+ + " alert(\"The username must be not null\");\n"
+ + " editconnection.username.focus();\n" + " return false;\n"
+ + " }\n" + " if (editconnection.password.value == \"\")\n" + " {\n"
+ + " alert(\"The password must be not null\");\n"
+ + " editconnection.password.focus();\n" + " return false;\n"
+ + " }\n" + " if (editconnection.endpoint.value == \"\")\n" + " {\n"
+ + " alert(\"The endpoint must be not null\");\n"
+ + " editconnection.endpoint.focus();\n" + " return false;\n"
+ + " }\n" + "\n" + " return true;\n" + "}\n" + " \n"
+ + "function checkConfigForSave()\n" + "{\n"
+ + " if (editconnection.username.value == \"\")\n" + " {\n"
+ + " alert(\"The username must be not null\");\n"
+ + " editconnection.username.focus();\n" + " return false;\n"
+ + " }\n" + " if (editconnection.password.value == \"\")\n" + " {\n"
+ + " alert(\"The password must be not null\");\n"
+ + " editconnection.password.focus();\n" + " return false;\n"
+ + " }\n" + " if (editconnection.endpoint.value == \"\")\n" + " {\n"
+ + " alert(\"The endpoint must be not null\");\n"
+ + " editconnection.endpoint.focus();\n" + " return false;\n"
+ + " }\n" + " if (editconnection.repositoryId.value == \"\")\n" + " {\n"
+ + " alert(\"The repository id must be not null\");\n"
+ + " editconnection.repositoryId.focus();\n" + " return false;\n"
+ + " }\n" + " return true;\n" + "}\n" + "\n" + "//-->\n"
+ + "</script>\n");
+ }
+
+ /**
+ * Process a configuration post. This method is called at the start of the
+ * connector's configuration 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 configuration parameters accordingly. The name
+ * of the posted form is "editconnection".
+ *
+ * @param threadContext
+ * is the local thread context.
+ * @param variableContext
+ * is the set of variables available from the post, including binary
+ * file post information.
+ * @param parameters
+ * are the configuration parameters, as they currently exist, for
+ * this connection being configured.
+ * @return null if all is well, or a string error message if there is an error
+ * that should prevent saving of the connection (and cause a
+ * redirection to an error page).
+ */
+ @Override
+ public String processConfigurationPost(IThreadContext threadContext,
+ IPostParameters variableContext, ConfigParams parameters)
+ throws ManifoldCFException {
+
+ //Repository
+ String username = variableContext.getParameter(CONFIG_PARAM_USERNAME);
+ if (StringUtils.isNotEmpty(username))
+ parameters.setParameter(CONFIG_PARAM_USERNAME, username);
+
+ String password = variableContext.getParameter(CONFIG_PARAM_PASSWORD);
+ if (StringUtils.isNotEmpty(password))
+ parameters.setParameter(CONFIG_PARAM_PASSWORD, password);
+
+ String endpoint = variableContext.getParameter(CONFIG_PARAM_ENDPOINT);
+ if (StringUtils.isNotEmpty(endpoint) && endpoint.length() > 0)
+ parameters.setParameter(CONFIG_PARAM_ENDPOINT, endpoint);
+
+ String repositoryId = variableContext
+ .getParameter(CONFIG_PARAM_REPOSITORY_ID);
+ if (StringUtils.isNotEmpty(repositoryId))
+ parameters.setParameter(CONFIG_PARAM_REPOSITORY_ID, repositoryId);
+
+ //User Mapping
+ String usernameRegexp = variableContext.getParameter(CONFIG_PARAM_USERNAME_REGEXP);
+ String userTranslation = variableContext.getParameter(CONFIG_PARAM_USER_TRANSLATION);
+ if (StringUtils.isNotEmpty(usernameRegexp) && StringUtils.isNotEmpty(userTranslation))
+ {
+ MatchMap localMap = new MatchMap();
+ localMap.appendMatchPair(usernameRegexp,userTranslation);
+ parameters.setParameter(CONFIG_PARAM_USERMAPPING,localMap.toString());
+ }
+
+ return null;
+
+ }
+
+ @Override
+ public String check() throws ManifoldCFException {
+ try {
+ checkConnection();
+ return super.check();
+ } catch (ServiceInterruption e) {
+ return "Connection temporarily failed: " + e.getMessage();
+ } catch (ManifoldCFException e) {
+ return "Connection failed: " + e.getMessage();
+ }
+ }
+
+ protected void checkConnection() throws ManifoldCFException,
+ ServiceInterruption {
+ while (true) {
+ boolean noSession = (session == null);
+ getSession();
+ long currentTime;
+ CheckConnectionThread t = new CheckConnectionThread();
+ try {
+ t.start();
+ t.join();
+ Throwable thr = t.getException();
+ if (thr != null) {
+ if (thr instanceof RemoteException)
+ throw (RemoteException) thr;
+ else
+ throw (Error) thr;
+ }
+ return;
+ } catch (InterruptedException e) {
+ t.interrupt();
+ throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+ ManifoldCFException.INTERRUPTED);
+ } catch (RemoteException e) {
+ Throwable e2 = e.getCause();
+ if (e2 instanceof InterruptedException
+ || e2 instanceof InterruptedIOException)
+ throw new ManifoldCFException(e2.getMessage(), e2,
+ ManifoldCFException.INTERRUPTED);
+ if (noSession) {
+ currentTime = System.currentTimeMillis();
+ throw new ServiceInterruption(
+ "Transient error connecting to filenet service: "
+ + e.getMessage(), currentTime + 60000L);
+ }
+ session = null;
+ lastSessionFetch = -1L;
+ continue;
+ }
+ }
+ }
+
+ protected void getSession() throws ManifoldCFException, ServiceInterruption {
+ if (session == null) {
+ // Check for parameter validity
+ if (StringUtils.isEmpty(username))
+ throw new ManifoldCFException("Parameter " + CONFIG_PARAM_USERNAME
+ + " required but not set");
+
+ if (Logging.connectors.isDebugEnabled())
+ Logging.connectors.debug("CMIS: Username = '" + username + "'");
+
+ if (StringUtils.isEmpty(password))
+ throw new ManifoldCFException("Parameter " + CONFIG_PARAM_PASSWORD
+ + " required but not set");
+
+ Logging.connectors.debug("CMIS: Password exists");
+
+ if (StringUtils.isEmpty(endpoint))
+ throw new ManifoldCFException("Parameter " + CONFIG_PARAM_ENDPOINT
+ + " required but not set");
+
+ long currentTime;
+ GetSessionThread t = new GetSessionThread();
+ try {
+ t.start();
+ t.join();
+ Throwable thr = t.getException();
+ if (thr != null) {
+ if (thr instanceof java.net.MalformedURLException)
+ throw (java.net.MalformedURLException) thr;
+ else if (thr instanceof NotBoundException)
+ throw (NotBoundException) thr;
+ else if (thr instanceof RemoteException)
+ throw (RemoteException) thr;
+ else
+ throw (Error) thr;
+ }
+ } catch (InterruptedException e) {
+ t.interrupt();
+ throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+ ManifoldCFException.INTERRUPTED);
+ } catch (java.net.MalformedURLException e) {
+ throw new ManifoldCFException(e.getMessage(), e);
+ } catch (NotBoundException e) {
+ // Transient problem: Server not available at the moment.
+ Logging.connectors.warn(
+ "CMIS: Server not up at the moment: " + e.getMessage(), e);
+ currentTime = System.currentTimeMillis();
+ throw new ServiceInterruption(e.getMessage(), currentTime + 60000L);
+ } catch (RemoteException e) {
+ Throwable e2 = e.getCause();
+ if (e2 instanceof InterruptedException
+ || e2 instanceof InterruptedIOException)
+ throw new ManifoldCFException(e2.getMessage(), e2,
+ ManifoldCFException.INTERRUPTED);
+ // Treat this as a transient problem
+ Logging.connectors.warn(
+ "CMIS: Transient remote exception creating session: "
+ + e.getMessage(), e);
+ currentTime = System.currentTimeMillis();
+ throw new ServiceInterruption(e.getMessage(), currentTime + 60000L);
+ }
+
+ }
+
+ lastSessionFetch = System.currentTimeMillis();
+ }
+
+ @Override
+ public void poll() throws ManifoldCFException {
+ if (lastSessionFetch == -1L)
+ return;
+
+ long currentTime = System.currentTimeMillis();
+ if (currentTime >= lastSessionFetch + timeToRelease) {
+ DestroySessionThread t = new DestroySessionThread();
+ try {
+ t.start();
+ t.join();
+ Throwable thr = t.getException();
+ if (thr != null) {
+ if (thr instanceof RemoteException)
+ throw (RemoteException) thr;
+ else
+ throw (Error) thr;
+ }
+ session = null;
+ lastSessionFetch = -1L;
+ } catch (InterruptedException e) {
+ t.interrupt();
+ throw new ManifoldCFException("Interrupted: " + e.getMessage(), e,
+ ManifoldCFException.INTERRUPTED);
+ } catch (RemoteException e) {
+ Throwable e2 = e.getCause();
+ if (e2 instanceof InterruptedException
+ || e2 instanceof InterruptedIOException)
+ throw new ManifoldCFException(e2.getMessage(), e2,
+ ManifoldCFException.INTERRUPTED);
+ session = null;
+ lastSessionFetch = -1L;
+ // Treat this as a transient problem
+ Logging.connectors.warn(
+ "CMIS: Transient remote exception closing session: "
+ + e.getMessage(), e);
+ }
+
+ }
+ }
+
+ /**
+ * View configuration. This method is called in the body section of the
+ * connector's view configuration page. Its purpose is to present the
+ * connection 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 threadContext
+ * is the local thread context.
+ * @param out
+ * is the output to which any HTML should be sent.
+ * @param parameters
+ * are the configuration parameters, as they currently exist, for
+ * this connection being configured.
+ */
+ @Override
+ public void viewConfiguration(IThreadContext threadContext, IHTTPOutput out,
+ ConfigParams parameters) throws ManifoldCFException, IOException {
+ out.print("<table class=\"displaytable\">\n"
+ + " <tr>\n"
+ + " <td class=\"description\" colspan=\"1\"><nobr>Parameters:</nobr></td>\n"
+ + " <td class=\"value\" colspan=\"3\">\n");
+ Iterator iter = parameters.listParameters();
+ while (iter.hasNext()) {
+ String param = (String) iter.next();
+ String value = parameters.getParameter(param);
+ if (param.length() >= "password".length()
+ && param.substring(param.length() - "password".length())
+ .equalsIgnoreCase("password")) {
+ out.print(" <nobr>"
+ + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param)
+ + "=********</nobr><br/>\n");
+ } else {
+ out.print(" <nobr>"
+ + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(param) + "="
+ + org.apache.manifoldcf.ui.util.Encoder.bodyEscape(value)
+ + "</nobr><br/>\n");
+ }
+ }
+ out.print("</td>\n" + " </tr>\n" + "</table>\n");
+ }
+
+ /** Obtain the default access tokens for a given user name.
+ *@param userName is the user name or identifier.
+ *@return the default response tokens, presuming that the connect method fails.
+ */
+ @Override
+ public AuthorizationResponse getDefaultAuthorizationResponse(String userName)
+ {
+ return unreachableResponse;
+ }
+
+ /** Uncached version of the getAuthorizationResponse method.
+ *@param userName is the user name or identifier.
+ *@return the response tokens (according to the current authority).
+ * (Should throws an exception only when a condition cannot be properly described within the authorization response object.)
+ */
+ protected AuthorizationResponse getAuthorizationResponseUncached(String userName)
+ throws ManifoldCFException
+ {
+ if (Logging.connectors.isDebugEnabled())
+ Logging.connectors.debug("CMIS: Calculating response access tokens for user '"+userName+"'");
+
+ // Map the user to the final value
+ String verifiedUserName = StringUtils.EMPTY;
+ try {
+ verifiedUserName = matchMap.translate(userName);
+ } catch (Exception e) {
+ return userNotFoundResponse;
+ }
+
+ if (Logging.connectors.isDebugEnabled())
+ Logging.connectors.debug("CMIS: Mapped user name is '"+verifiedUserName+"'");
+
+ String[] tokens = new String[1];
+ tokens[0] = verifiedUserName;
+ return new AuthorizationResponse(tokens,AuthorizationResponse.RESPONSE_OK);
+
+ }
+
+ /** Obtain the access tokens for a given user name.
+ *@param userName is the user name or identifier.
+ *@return the response tokens (according to the current authority).
+ * (Should throws an exception only when a condition cannot be properly described within the authorization response object.)
+ */
+ @Override
+ public AuthorizationResponse getAuthorizationResponse(String userName)
+ throws ManifoldCFException
+ {
+ if (Logging.connectors.isDebugEnabled())
+ Logging.connectors.debug("CMIS: Received request for user '"+userName+"'");
+
+ ICacheDescription objectDescription = new AuthorizationResponseDescription(userName,endpoint,repositoryId,matchMap.toString());
+
+ // Enter the cache
+ ICacheHandle ch = cacheManager.enterCache(new ICacheDescription[]{objectDescription},null,null);
+ try
+ {
+ ICacheCreateHandle createHandle = cacheManager.enterCreateSection(ch);
+ try
+ {
+ // Lookup the object
+ AuthorizationResponse response = (AuthorizationResponse)cacheManager.lookupObject(createHandle,objectDescription);
+ if (response != null)
+ return response;
+ // Create the object.
+ response = getAuthorizationResponseUncached(userName);
+ // Save it in the cache
+ cacheManager.saveObject(createHandle,objectDescription,response);
+ // And return it...
+ return response;
+ }
+ finally
+ {
+ cacheManager.leaveCreateSection(createHandle);
+ }
+ }
+ finally
+ {
+ cacheManager.leaveCache(ch);
+ }
+ }
+
+ /** This is the cache object descriptor for cached access tokens from
+ * this connector.
+ */
+ protected static class AuthorizationResponseDescription extends org.apache.manifoldcf.core.cachemanager.BaseDescription
+ {
+ /** The user name associated with the access tokens */
+ protected String userName;
+ /** The repository endpoint */
+ protected String endpoint;
+ /** The repository id */
+ protected String repositoryId;
+ /** The user mapping */
+ protected String userMapping;
+ /** The expiration time */
+ protected long expirationTime = -1;
+
+ /** Constructor. */
+ public AuthorizationResponseDescription(String userName, String endpoint, String repositoryId, String userMapping)
+ {
+ super("CMISAuthority",LRUsize);
+ this.userName = userName;
+ this.endpoint = endpoint;
+ this.repositoryId = repositoryId;
+ this.userMapping = userMapping;
+ }
+
+ /** Return the invalidation keys for this object. */
+ public StringSet getObjectKeys()
+ {
+ return emptyStringSet;
+ }
+
+ /** Get the critical section name, used for synchronizing the creation of the object */
+ public String getCriticalSectionName()
+ {
+ return getClass().getName() + "-" + userName + "-" + endpoint +
+ "-" + repositoryId + "_" + userMapping;
+ }
+
+ /** Return the object expiration interval */
+ public long getObjectExpirationTime(long currentTime)
+ {
+ if (expirationTime == -1)
+ expirationTime = currentTime + responseLifetime;
+ return expirationTime;
+ }
+
+ public int hashCode()
+ {
+ return userName.hashCode() + endpoint.hashCode() + repositoryId.hashCode() + userMapping.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof AuthorizationResponseDescription))
+ return false;
+ AuthorizationResponseDescription ard = (AuthorizationResponseDescription)o;
+ return ard.userName.equals(userName) && ard.endpoint.equals(endpoint) &&
+ repositoryId.equals(repositoryId) && ard.userMapping.equals(userMapping);
+ }
+
+ }
+
+}
Propchange: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisAuthorityConnector.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/CmisAuthorityConnector.java
------------------------------------------------------------------------------
svn:keywords = Id
Added: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/MatchMap.java
URL: http://svn.apache.org/viewvc/incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/MatchMap.java?rev=1148064&view=auto
==============================================================================
--- incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/MatchMap.java (added)
+++ incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/MatchMap.java Mon Jul 18 21:40:19 2011
@@ -0,0 +1,587 @@
+/* $Id$ */
+
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache 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.apache.org/licenses/LICENSE-2.0
+*
+* 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.apache.manifoldcf.crawler.connectors.cmis;
+
+import org.apache.manifoldcf.core.interfaces.*;
+import java.util.*;
+import java.util.regex.*;
+
+/** An instance of this class describes a "match map", which describes a translation of an input
+* string using regexp technology.
+* A match map consists of multiple clauses, which are fired in sequence. Each clause is a regexp
+* search and replace, where the replace string can include references to the groups present in the
+* search regexp.
+* MatchMaps can be converted to strings in two different ways. The first way is to build a single
+* string of the form "match1=replace1&match2=replace2...". Strings of this kind must escape & and =
+* characters in the match and replace strings, where found. The second way is to generate an array
+* of match strings and a corresponding array of replace strings. This method requires no escaping
+* of the string contents.
+*/
+public class MatchMap
+{
+ public static final String _rcsid = "@(#)$Id$";
+
+ /** This is the set of match regexp strings */
+ protected ArrayList matchStrings;
+ /** This is the set of Pattern objects corresponding to the match regexp strings.
+ * It's null if the patterns have not been built yet. */
+ protected Pattern[] matchPatterns = null;
+ /** This is the set of replace strings */
+ protected ArrayList replaceStrings;
+
+ /** Constructor. Build an empty matchmap. */
+ public MatchMap()
+ {
+ matchStrings = new ArrayList();
+ replaceStrings = new ArrayList();
+ }
+
+ /** Constructor. Build a matchmap from a single string. */
+ public MatchMap(String stringForm)
+ {
+ matchStrings = new ArrayList();
+ replaceStrings = new ArrayList();
+ StringBuffer matchString = new StringBuffer();
+ StringBuffer replaceString = new StringBuffer();
+ int i = 0;
+ while (i < stringForm.length())
+ {
+ matchString.setLength(0);
+ replaceString.setLength(0);
+ while (i < stringForm.length())
+ {
+ char x = stringForm.charAt(i);
+ if (x == '&' || x == '=')
+ break;
+ i++;
+ if (x == '\\' && i < stringForm.length())
+ x = stringForm.charAt(i++);
+ matchString.append(x);
+ }
+
+ if (i < stringForm.length())
+ {
+ char x = stringForm.charAt(i);
+ if (x == '=')
+ {
+ i++;
+ // Pick up the second string
+ while (i < stringForm.length())
+ {
+ x = stringForm.charAt(i);
+ if (x == '&')
+ break;
+ i++;
+ if (x == '\\' && i < stringForm.length())
+ x = stringForm.charAt(i++);
+ replaceString.append(x);
+ }
+ }
+ }
+
+ matchStrings.add(matchString.toString());
+ replaceStrings.add(replaceString.toString());
+
+ if (i < stringForm.length())
+ {
+ char x = stringForm.charAt(i);
+ if (x == '&')
+ i++;
+ }
+ }
+ }
+
+ /** Constructor. Build a matchmap from two arraylists representing match and replace strings */
+ public MatchMap(ArrayList matchStrings, ArrayList replaceStrings)
+ {
+ this.matchStrings = (ArrayList)matchStrings.clone();
+ this.replaceStrings = (ArrayList)replaceStrings.clone();
+ }
+
+ /** Get the number of match/replace strings */
+ public int getMatchCount()
+ {
+ return matchStrings.size();
+ }
+
+ /** Get a specific match string */
+ public String getMatchString(int index)
+ {
+ return (String)matchStrings.get(index);
+ }
+
+ /** Get a specific replace string */
+ public String getReplaceString(int index)
+ {
+ return (String)replaceStrings.get(index);
+ }
+
+ /** Delete a specified match/replace string pair */
+ public void deleteMatchPair(int index)
+ {
+ matchStrings.remove(index);
+ replaceStrings.remove(index);
+ matchPatterns = null;
+ }
+
+ /** Insert a match/replace string pair */
+ public void insertMatchPair(int index, String match, String replace)
+ {
+ matchStrings.add(index,match);
+ replaceStrings.add(index,replace);
+ matchPatterns = null;
+ }
+
+ /** Append a match/replace string pair */
+ public void appendMatchPair(String match, String replace)
+ {
+ matchStrings.add(match);
+ replaceStrings.add(replace);
+ matchPatterns = null;
+ }
+
+ /** Append old-style match/replace pair.
+ * This method translates old-style regexp and group output form to the
+ * current style before adding to the map.
+ */
+ public void appendOldstyleMatchPair(String oldstyleMatch, String oldstyleReplace)
+ {
+ String newStyleMatch = "^" + oldstyleMatch + "$";
+
+ // Need to build a new-style replace string from the old one. To do that, use the
+ // original parser (which basically will guarantee that we get it right)
+
+ EvaluatorTokenStream et = new EvaluatorTokenStream(oldstyleReplace);
+ StringBuffer newStyleReplace = new StringBuffer();
+
+ while (true)
+ {
+ EvaluatorToken t = et.peek();
+ if (t == null)
+ break;
+ switch (t.getType())
+ {
+ case EvaluatorToken.TYPE_COMMA:
+ et.advance();
+ break;
+ case EvaluatorToken.TYPE_GROUP:
+ et.advance();
+ int groupNumber = t.getGroupNumber();
+ switch (t.getGroupStyle())
+ {
+ case EvaluatorToken.GROUPSTYLE_NONE:
+ newStyleReplace.append("$(").append(Integer.toString(groupNumber)).append(")");
+ break;
+ case EvaluatorToken.GROUPSTYLE_LOWER:
+ newStyleReplace.append("$(").append(Integer.toString(groupNumber)).append("l)");
+ break;
+ case EvaluatorToken.GROUPSTYLE_UPPER:
+ newStyleReplace.append("$(").append(Integer.toString(groupNumber)).append("u)");
+ break;
+ case EvaluatorToken.GROUPSTYLE_MIXED:
+ newStyleReplace.append("$(").append(Integer.toString(groupNumber)).append("m)");
+ break;
+ default:
+ break;
+ }
+ break;
+ case EvaluatorToken.TYPE_TEXT:
+ et.advance();
+ escape(newStyleReplace,t.getTextValue());
+ break;
+ default:
+ break;
+ }
+ }
+
+ appendMatchPair(newStyleMatch,newStyleReplace.toString());
+ }
+
+ /** Escape a string so it is verbatim */
+ protected static void escape(StringBuffer output, String input)
+ {
+ int i = 0;
+ while (i < input.length())
+ {
+ char x = input.charAt(i++);
+ if (x == '$')
+ output.append(x);
+ output.append(x);
+ }
+ }
+
+ /** Convert the matchmap to string form. */
+ public String toString()
+ {
+ int i = 0;
+ StringBuffer rval = new StringBuffer();
+ while (i < matchStrings.size())
+ {
+ String matchString = (String)matchStrings.get(i);
+ String replaceString = (String)replaceStrings.get(i);
+ if (i > 0)
+ rval.append('&');
+ stuff(rval,matchString);
+ rval.append('=');
+ stuff(rval,replaceString);
+ i++;
+ }
+ return rval.toString();
+ }
+
+ /** Stuff characters */
+ protected static void stuff(StringBuffer sb, String value)
+ {
+ int i = 0;
+ while (i < value.length())
+ {
+ char x = value.charAt(i++);
+ if (x == '\\' || x == '&' || x == '=')
+ sb.append('\\');
+ sb.append(x);
+ }
+ }
+
+ /** Perform a translation.
+ */
+ public String translate(String input)
+ throws ManifoldCFException
+ {
+ // Build pattern vector if not already there
+ if (matchPatterns == null)
+ {
+ matchPatterns = new Pattern[matchStrings.size()];
+ int i = 0;
+ while (i < matchPatterns.length)
+ {
+ String regexp = (String)matchStrings.get(i);
+ try
+ {
+ matchPatterns[i] = Pattern.compile(regexp);
+ }
+ catch (java.util.regex.PatternSyntaxException e)
+ {
+ matchPatterns = null;
+ throw new ManifoldCFException("For match expression '"+regexp+"', found pattern syntax error: "+e.getMessage(),e);
+ }
+ i++;
+ }
+ }
+
+ int j = 0;
+ while (j < matchPatterns.length)
+ {
+ Pattern p = matchPatterns[j];
+ // Construct a matcher
+ Matcher m = p.matcher(input);
+ // Grab the output description
+ String outputDescription = (String)replaceStrings.get(j);
+ j++;
+ // Create a copy buffer
+ StringBuffer outputBuffer = new StringBuffer();
+ // Keep track of the index in the original string we have done up to
+ int currentIndex = 0;
+ // Scan the string using find, and for each one found, do a translation
+ while (true)
+ {
+ boolean foundOne = m.find();
+ if (foundOne == false)
+ {
+ // No subsequent match found.
+ // Copy everything from currentIndex until the end of input
+ outputBuffer.append(input.substring(currentIndex));
+ break;
+ }
+
+ // Do a translation. This involves copying everything in the input
+ // string up until the start of the match, then doing a replace for
+ // the match itself, and finally setting the currentIndex to the end
+ // of the match.
+
+ int matchStart = m.start(0);
+ int matchEnd = m.end(0);
+ if (matchStart == -1)
+ {
+ // The expression was degenerate; treat this as the end.
+ outputBuffer.append(input.substring(currentIndex));
+ break;
+ }
+ outputBuffer.append(input.substring(currentIndex,matchStart));
+
+ // Process translation description!
+ int i = 0;
+ while (i < outputDescription.length())
+ {
+ char x = outputDescription.charAt(i++);
+ if (x == '$' && i < outputDescription.length())
+ {
+ x = outputDescription.charAt(i++);
+ if (x == '(')
+ {
+ // Process evaluation expression
+ StringBuffer numberBuf = new StringBuffer();
+ boolean upper = false;
+ boolean lower = false;
+ boolean mixed = false;
+ while (i < outputDescription.length())
+ {
+ char y = outputDescription.charAt(i++);
+ if (y == ')')
+ break;
+ else if (y >= '0' && y <= '9')
+ numberBuf.append(y);
+ else if (y == 'u' || y == 'U')
+ upper = true;
+ else if (y == 'l' || y == 'L')
+ lower = true;
+ else if (y == 'm' || y == 'M')
+ mixed = true;
+ }
+ String number = numberBuf.toString();
+ try
+ {
+ int groupnum = Integer.parseInt(number);
+ String groupValue = m.group(groupnum);
+ if (upper)
+ outputBuffer.append(groupValue.toUpperCase());
+ else if (lower)
+ outputBuffer.append(groupValue.toLowerCase());
+ else if (mixed && groupValue.length() > 0)
+ outputBuffer.append(groupValue.substring(0,1).toUpperCase()).append(groupValue.substring(1).toLowerCase());
+ else
+ outputBuffer.append(groupValue);
+
+ }
+ catch (NumberFormatException e)
+ {
+ // Silently skip, because it's an illegal group number, so nothing
+ // gets added.
+ }
+
+ // Go back around, so we don't add the $ in
+ continue;
+ }
+ }
+ outputBuffer.append(x);
+ }
+
+ currentIndex = matchEnd;
+ }
+
+ input = outputBuffer.toString();
+ }
+
+ return input;
+ }
+
+
+ // Protected classes
+
+ // These classes are used to process the old token-based replacement strings
+
+ /** Evaluator token.
+ */
+ protected static class EvaluatorToken
+ {
+ public final static int TYPE_GROUP = 0;
+ public final static int TYPE_TEXT = 1;
+ public final static int TYPE_COMMA = 2;
+
+ public final static int GROUPSTYLE_NONE = 0;
+ public final static int GROUPSTYLE_LOWER = 1;
+ public final static int GROUPSTYLE_UPPER = 2;
+ public final static int GROUPSTYLE_MIXED = 3;
+
+ protected int type;
+ protected int groupNumber = -1;
+ protected int groupStyle = GROUPSTYLE_NONE;
+ protected String textValue = null;
+
+ public EvaluatorToken()
+ {
+ type = TYPE_COMMA;
+ }
+
+ public EvaluatorToken(int groupNumber, int groupStyle)
+ {
+ type = TYPE_GROUP;
+ this.groupNumber = groupNumber;
+ this.groupStyle = groupStyle;
+ }
+
+ public EvaluatorToken(String text)
+ {
+ type = TYPE_TEXT;
+ this.textValue = text;
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public int getGroupNumber()
+ {
+ return groupNumber;
+ }
+
+ public int getGroupStyle()
+ {
+ return groupStyle;
+ }
+
+ public String getTextValue()
+ {
+ return textValue;
+ }
+
+ }
+
+
+ /** Token stream.
+ */
+ protected static class EvaluatorTokenStream
+ {
+ protected String text;
+ protected int pos;
+ protected EvaluatorToken token = null;
+
+ /** Constructor.
+ */
+ public EvaluatorTokenStream(String text)
+ {
+ this.text = text;
+ this.pos = 0;
+ }
+
+ /** Get current token.
+ */
+ public EvaluatorToken peek()
+ {
+ if (token == null)
+ {
+ token = nextToken();
+ }
+ return token;
+ }
+
+ /** Go on to next token.
+ */
+ public void advance()
+ {
+ token = null;
+ }
+
+ protected EvaluatorToken nextToken()
+ {
+ char x;
+ // Fetch the next token
+ while (true)
+ {
+ if (pos == text.length())
+ return null;
+ x = text.charAt(pos);
+ if (x > ' ')
+ break;
+ pos++;
+ }
+
+ StringBuffer sb;
+
+ if (x == '"')
+ {
+ // Parse text
+ pos++;
+ sb = new StringBuffer();
+ while (true)
+ {
+ if (pos == text.length())
+ break;
+ x = text.charAt(pos);
+ pos++;
+ if (x == '"')
+ {
+ break;
+ }
+ if (x == '\\')
+ {
+ if (pos == text.length())
+ break;
+ x = text.charAt(pos++);
+ }
+ sb.append(x);
+ }
+
+ return new EvaluatorToken(sb.toString());
+ }
+
+ if (x == ',')
+ {
+ pos++;
+ return new EvaluatorToken();
+ }
+
+ // Eat number at beginning
+ sb = new StringBuffer();
+ while (true)
+ {
+ if (pos == text.length())
+ break;
+ x = text.charAt(pos);
+ if (x >= '0' && x <= '9')
+ {
+ sb.append(x);
+ pos++;
+ continue;
+ }
+ break;
+ }
+ String numberValue = sb.toString();
+ int groupNumber = 0;
+ if (numberValue.length() > 0)
+ groupNumber = new Integer(numberValue).intValue();
+ // Save the next char position
+ int modifierPos = pos;
+ // Go to the end of the word
+ while (true)
+ {
+ if (pos == text.length())
+ break;
+ x = text.charAt(pos);
+ if (x == ',' || x >= '0' && x <= '9' || x <= ' ' && x >= 0)
+ break;
+ pos++;
+ }
+
+ int style = EvaluatorToken.GROUPSTYLE_NONE;
+ if (modifierPos != pos)
+ {
+ String modifier = text.substring(modifierPos,pos);
+ if (modifier.startsWith("u"))
+ style = EvaluatorToken.GROUPSTYLE_UPPER;
+ else if (modifier.startsWith("l"))
+ style = EvaluatorToken.GROUPSTYLE_LOWER;
+ else if (modifier.startsWith("m"))
+ style = EvaluatorToken.GROUPSTYLE_MIXED;
+ }
+ return new EvaluatorToken(groupNumber,style);
+ }
+ }
+
+}
Propchange: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/MatchMap.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/lcf/branches/CONNECTORS-221/connectors/cmis/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/cmis/MatchMap.java
------------------------------------------------------------------------------
svn:keywords = Id