Java tutorial
/******************************************************************************* * Copyright (c) 2012, 2013 Ericsson AB and others. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Description: * * Contributors: * Miles Parker, Tasktop Technologies - Initial API and Implementation * Others (includes code modified from GerritConnector in org.eclipse.mylyn.gerit, see author annotations.) *******************************************************************************/ package org.eclipse.mylyn.reviews.connector; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Calendar; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EEnum; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EFactory; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.mylyn.internal.reviews.connector.EmfCorePlugin; import org.eclipse.mylyn.internal.tasks.core.RepositoryTaskHandleUtil; import org.eclipse.mylyn.reviews.connector.EmfTaskSchema.FieldFeature; import org.eclipse.mylyn.reviews.connector.client.EmfClient; import org.eclipse.mylyn.reviews.connector.query.EmfQueryEngine; import org.eclipse.mylyn.reviews.connector.query.QueryClause; import org.eclipse.mylyn.reviews.connector.query.QueryException; import org.eclipse.mylyn.reviews.connector.query.SimpleQueryEngine; import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector; import org.eclipse.mylyn.tasks.core.IRepositoryQuery; import org.eclipse.mylyn.tasks.core.ITask; import org.eclipse.mylyn.tasks.core.ITaskMapping; import org.eclipse.mylyn.tasks.core.RepositoryClientManager; import org.eclipse.mylyn.tasks.core.RepositoryResponse; import org.eclipse.mylyn.tasks.core.RepositoryResponse.ResponseKind; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.core.data.AbstractTaskDataHandler; import org.eclipse.mylyn.tasks.core.data.AbstractTaskSchema.Field; import org.eclipse.mylyn.tasks.core.data.TaskAttribute; import org.eclipse.mylyn.tasks.core.data.TaskAttributeMapper; import org.eclipse.mylyn.tasks.core.data.TaskData; import org.eclipse.mylyn.tasks.core.data.TaskDataCollector; import org.eclipse.mylyn.tasks.core.data.TaskMapper; import org.eclipse.mylyn.tasks.core.sync.ISynchronizationSession; import org.osgi.framework.Bundle; /** * Generic support for connectors to Emf models. Connector implementors should override, providing all meta-data. * * @author Miles Parker */ @SuppressWarnings("restriction") public abstract class AbstractEmfConnector extends AbstractRepositoryConnector { /** * Unicode en-dash. */ public static final String EN_DASH = "\u2013"; //$NON-NLS-1$ public static final String ITEM_DELIM = "#"; //$NON-NLS-1$ public enum KeyStrategy { EMF_SCHEMA_FIELD, OBJECT_ID, MEMBER_INDEX, XMI_ID }; class EmfClientManager extends RepositoryClientManager<EmfClient, EmfConfiguration> { public EmfClientManager() { super(getCacheFile(), getConfigurationClass()); } @Override protected EmfClient createClient(TaskRepository repository, EmfConfiguration configuration) { return AbstractEmfConnector.this.createClient(repository, configuration); } } /** * By delegating back to the EmfConnector for this case, we can greatly simplify API for consumers. */ class EmfTaskDataHandler extends AbstractTaskDataHandler { @Override public TaskAttributeMapper getAttributeMapper(TaskRepository repository) { return AbstractEmfConnector.this.getAttributeMapper(repository); } @Override public boolean initializeTaskData(TaskRepository repository, TaskData taskData, ITaskMapping initializationData, IProgressMonitor monitor) { return AbstractEmfConnector.this.initializeTaskData(repository, taskData, initializationData, monitor); } @Override public RepositoryResponse postTaskData(TaskRepository repository, TaskData taskData, Set<TaskAttribute> oldAttributes, IProgressMonitor monitor) throws CoreException { return AbstractEmfConnector.this.postTaskData(repository, taskData, oldAttributes, monitor); } } /** * By delegating back to the EmfConnector for this case, we can greatly simplify API for consumers. */ public class EmfTaskSchemaDelegator extends EmfTaskSchema { @Override protected EClass[] getSchemaEClasses() { return AbstractEmfConnector.this.getTaskClasses(); } @Override protected FieldFeature[] getSchemaPairs() { return AbstractEmfConnector.this.getTaskFeatures(); } } public static final String EMF_ITEM_DELIM = "#"; //$NON-NLS-1$ private final EmfTaskDataHandler taskDataHandler = new EmfTaskDataHandler(); private RepositoryClientManager<EmfClient, EmfConfiguration> clientManager; private EmfTaskSchema taskSchema; private final File configurationCacheFile; private final Set<QueryClause> queryClauses = new HashSet<QueryClause>(); public AbstractEmfConnector() { IPath stateLocation = Platform.getStateLocation(EmfCorePlugin.getDefault().getBundle()); IPath cache = stateLocation.append("repositoryConfigurations"); //$NON-NLS-1$ configurationCacheFile = cache.toFile(); } public Class<EmfConfiguration> getConfigurationClass() { return EmfConfiguration.class; } /** * Not supported yet. */ @Override public boolean canCreateNewTask(TaskRepository arg0) { return true; } @Override public boolean canCreateTaskFromKey(TaskRepository arg0) { return true; } public EmfClient getClient(TaskRepository repository) { return getClientManager().getClient(repository); } public TaskAttributeMapper getAttributeMapper(TaskRepository repository) { return getSchema().getAttributeMapper(repository); } public EmfAttributeMapper getEmfMapper(TaskRepository repository) { return (EmfAttributeMapper) getAttributeMapper(repository); } public EmfQueryEngine getQueryEngine(TaskRepository repository) { return new SimpleQueryEngine(this, repository); } public boolean initializeTaskData(TaskRepository repository, TaskData data, ITaskMapping initializationData, IProgressMonitor monitor) { getSchema().initialize(data); String taskId = data.getTaskId(); data.getRoot().createAttribute(TaskAttribute.TASK_URL) .setValue(getTaskUrl(repository.getRepositoryUrl(), taskId)); data.getRoot().createAttribute(TaskAttribute.TASK_KEY).setValue(encodeTaskKey(taskId)); return true; } public RepositoryResponse postTaskData(TaskRepository repository, TaskData taskData, Set<TaskAttribute> oldAttributes, IProgressMonitor monitor) throws CoreException { EmfClient client = getClient(repository); client.open(); String taskId = taskData.getTaskId(); EObject emfTask; if (taskId.equals("")) { //$NON-NLS-1$ EClass containedClass = getTaskClasses()[0]; @SuppressWarnings("unchecked") EList<? super EObject> taskContainment = (EList<? super EObject>) client.getRootContainer() .eGet(getContainmentReference()); emfTask = client.create(containedClass, taskData); taskContainment.add(emfTask); //Emf implementation may create a key at model save time (e.g. XMLResource uuid implementation) client.save(); taskId = getTaskKey(repository, emfTask); if (StringUtils.isEmpty(taskId) || taskId.equals("0")) { //$NON-NLS-1$ taskId = getNextTaskId(client.getRootContainer()); if (taskId != null) { TaskAttribute keyAttribute = taskData.getRoot().getAttribute(TaskAttribute.TASK_KEY); keyAttribute.setValue(taskId); oldAttributes.add(keyAttribute); } } for (Field field : getSchema().getFields()) { oldAttributes.add(taskData.getRoot().getAttribute(field.getKey())); } } else { emfTask = getTaskObjectChecked(repository, taskId, monitor); } boolean emfDirty = false; for (TaskAttribute staleAttribute : oldAttributes) { String id = staleAttribute.getId(); TaskAttribute newAttribute = taskData.getRoot().getAttribute(id); boolean isSet = newAttribute.getValues().size() > 0; if (isSet) { boolean setValueForKey = getEmfMapper(repository).copyTaskToEmf(newAttribute, emfTask); emfDirty |= setValueForKey; } } if (emfDirty) { Date time = Calendar.getInstance().getTime(); EStructuralFeature dateModificationFeature = getSchema().getFeature(TaskAttribute.DATE_MODIFICATION); emfTask.eSet(dateModificationFeature, time); client.notifyChanged(emfTask); } return new RepositoryResponse(ResponseKind.TASK_UPDATED, taskId); } public TaskData createTaskData(TaskRepository repository, String taskId, IProgressMonitor monitor) { TaskData data = new TaskData(getAttributeMapper(repository), getConnectorKind(), repository.getRepositoryUrl(), taskId); initializeTaskData(repository, data, null, monitor); return data; } public TaskData createPartialTaskData(TaskRepository repository, String taskId, IProgressMonitor monitor) { TaskData data = new TaskData(getAttributeMapper(repository), getConnectorKind(), repository.getRepositoryUrl(), taskId); //Only create summary and other mapped attributes now getSchema().initialize(data); data.setPartial(true); return data; } /** * Retrieves task data for the given review from repository. */ @Override public TaskData getTaskData(TaskRepository repository, String taskId, IProgressMonitor monitor) throws CoreException { EmfClient client = getClient(repository); client.updateConfiguration(monitor); EObject emfObject = getTaskObjectChecked(repository, taskId, monitor); String id = getTaskKey(repository, emfObject); TaskData data = createTaskData(repository, id, monitor); EmfTaskSchema schema = getSchema(); for (Field field : schema.getFields()) { EStructuralFeature feature = schema.getFeature(field.getKey()); //TODO support object references if (feature instanceof EAttribute && !((EAttribute) feature).isMany()) { EAttribute emfAttribute = (EAttribute) feature; Object emfValue = emfObject.eGet(feature); TaskAttribute taskAttribute = field.createAttribute(data.getRoot()); if (emfAttribute.getEAttributeType() instanceof EEnum) { EEnum enumerator = (EEnum) emfAttribute.getEAttributeType(); for (EEnumLiteral literal : enumerator.getELiterals()) { taskAttribute.putOption(literal.getLiteral(), literal.getName()); } } getEmfMapper(repository).copyEmfToTask(emfObject, taskAttribute); if (emfValue != null) { EFactory factory = emfAttribute.getEAttributeType().getEPackage().getEFactoryInstance(); String stringValue = factory.convertToString(emfAttribute.getEAttributeType(), emfValue); taskAttribute.setValue(stringValue); } else if (!emfObject.eIsSet(feature)) { taskAttribute.clearValues(); } else { taskAttribute.setValue(""); //$NON-NLS-1$ } } } return data; } protected KeyStrategy getKeyStrategy() { return KeyStrategy.EMF_SCHEMA_FIELD; } protected String getTaskKey(TaskRepository repository, EObject emfObject) { String key = null; switch (getKeyStrategy()) { case EMF_SCHEMA_FIELD: key = getEmfMapper(repository).getEmfString(emfObject, TaskAttribute.TASK_KEY); break; case OBJECT_ID: key = EcoreUtil.getID(emfObject); break; case XMI_ID: Resource resource = emfObject.eResource(); if (resource instanceof XMLResource) { XMLResource xmlResource = (XMLResource) resource; key = xmlResource.getID(emfObject); } break; case MEMBER_INDEX: EObject rootContainer = EcoreUtil.getRootContainer(emfObject); String uriFragment = rootContainer.eResource().getURIFragment(emfObject); key = uriFragment; int dotIndex = key.indexOf('.'); if (dotIndex >= 0) { key = key.substring(dotIndex + 1); } break; } return encodeTaskKey(key); } public EObject getTaskObject(TaskRepository repository, String taskId, IProgressMonitor monitor) throws CoreException { taskId = encodeTaskKey(taskId); EmfClient client = getClient(repository); client.open(); EObject container = client.getRootContainer(); EObject rootContainer = EcoreUtil.getRootContainer(container); EObject referencedObject = rootContainer.eResource().getEObject(taskId); taskId = encodeTaskKey(taskId); if (referencedObject == null) { for (Object object : ((List<?>) container.eGet(getContainmentReference()))) { if (object instanceof EObject) { EObject eObject = (EObject) object; String stringValueForKey = getTaskKey(repository, eObject); if (ObjectUtils.equals(stringValueForKey, taskId)) { referencedObject = eObject; break; } } } } return referencedObject; } public EObject getTaskObjectChecked(TaskRepository repository, String taskId, IProgressMonitor monitor) throws CoreException { EObject emfObject = getTaskObject(repository, taskId, monitor); if (emfObject == null) { throw new CoreException(new Status(IStatus.WARNING, EmfCorePlugin.PLUGIN_ID, "Couldn't locate task object for taskId: " + taskId //$NON-NLS-1$ )); } return emfObject; } @Override public AbstractTaskDataHandler getTaskDataHandler() { return taskDataHandler; } /** * Encode a task id to prevent use of handle delimiters. * * @param taskId * @return */ public String encodeTaskKey(String taskId) { return StringUtils.replace(taskId, RepositoryTaskHandleUtil.HANDLE_DELIM, EN_DASH); } /** * Decode a task id to restore any handle delimiters. * * @param taskId * @return */ public String decodeTaskKey(String taskId) { return StringUtils.replace(taskId, EN_DASH, RepositoryTaskHandleUtil.HANDLE_DELIM); } @Override public String getRepositoryUrlFromTaskUrl(String url) { if (url == null) { return null; } int i = url.indexOf(ITEM_DELIM); if (i != -1) { return url.substring(0, i); } return null; } @Override public String getTaskIdFromTaskUrl(String url) { if (url == null) { return null; } int index = url.indexOf(ITEM_DELIM); if (index > 0) { String taskId = url.substring(index + ITEM_DELIM.length()); try { taskId = URLDecoder.decode(taskId, "utf-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } return taskId; } return null; } @Override public String getTaskUrl(String repositoryUrl, String taskId) { String encodedId = taskId; try { encodedId = URLEncoder.encode(taskId, "utf-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } return repositoryUrl + ITEM_DELIM + encodedId; } @Override public ITaskMapping getTaskMapping(TaskData taskData) { return new TaskMapper(taskData); } @Override public boolean hasTaskChanged(TaskRepository repository, ITask task, TaskData taskData) { ITaskMapping taskMapping = getTaskMapping(taskData); if (taskData.isPartial() && task.getModificationDate() != null) { return false; } Date repositoryDate = taskMapping.getModificationDate(); Date localDate = task.getModificationDate(); boolean hasChanged = repositoryDate != null && repositoryDate.equals(localDate); return hasChanged; } @Override public IStatus performQuery(TaskRepository repository, IRepositoryQuery query, TaskDataCollector resultCollector, ISynchronizationSession session, IProgressMonitor monitor) { try { EmfClient client = getClient(repository); client.open(); List<EObject> results; try { results = getQueryEngine(repository).performQuery(query, monitor); for (EObject taskObject : results) { String id = encodeTaskKey(getTaskKey(repository, taskObject)); if (!StringUtils.isEmpty(id)) { TaskData taskData = createPartialTaskData(repository, id, monitor); resultCollector.accept(taskData); } } return Status.OK_STATUS; } catch (QueryException e) { return new Status(IStatus.ERROR, EmfCorePlugin.PLUGIN_ID, "Problem occurred while executing query.", //$NON-NLS-1$ e); } } catch (CoreException e) { return e.getStatus(); } finally { monitor.done(); } } @Override public void updateRepositoryConfiguration(TaskRepository repository, IProgressMonitor monitor) throws CoreException { getClient(repository).updateConfiguration(monitor); } @Override public void updateTaskFromTaskData(TaskRepository taskRepository, ITask task, TaskData taskData) { Date oldModificationDate = task.getModificationDate(); TaskMapper mapper = (TaskMapper) getTaskMapping(taskData); // retain modification date to force an update when full task data is received if (taskData.isPartial()) { task.setModificationDate(oldModificationDate); } else { mapper.applyTo(task); } } public IStatus validate(TaskRepository repository, IProgressMonitor monitor) throws CoreException { return Status.OK_STATUS; } public RepositoryClientManager<EmfClient, EmfConfiguration> getClientManager() { if (clientManager == null) { clientManager = new EmfClientManager(); } return clientManager; } public final EmfTaskSchema getSchema() { if (taskSchema == null) { taskSchema = createTaskSchema(); taskSchema.initialize(); } return taskSchema; } /** * Override to provide a custom task schema implementation. (The default Schema will provide values based on the * connector meta-data .) * * @return */ public EmfTaskSchema createTaskSchema() { return new EmfTaskSchemaDelegator(); } public File getCacheFile() { return configurationCacheFile; } /** * Override to implement a custom EMF client. In most cases this should not be necessary. * * @param repository * @param configuration * @return */ protected EmfClient createClient(TaskRepository repository, EmfConfiguration configuration) { return new EmfClient(repository, configuration) { @Override public AbstractEmfConnector getConnector() { return AbstractEmfConnector.this; } }; } public Set<QueryClause> getQueryClauses() { return queryClauses; } public abstract String getNextTaskId(EObject taskContainer); public abstract EAttribute getContentsNameAttribute(); public abstract EReference getContainmentReference(); /** * The class to be used for task container. By default this is the clss defined by the containment reference. * * @return */ public EClass getContainerClass() { return getContainmentReference().getEContainingClass(); } public abstract EAttribute[] getSearchAttributes(); public abstract Bundle getConnectorBundle(); public abstract EClass[] getTaskClasses(); public abstract FieldFeature[] getTaskFeatures(); }