/******************************************************************************* * Copyright (c) 2004, 2013 Tasktop Technologies 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 * * Contributors: * Tasktop Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.tasks.core.data; import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXTransformerFactory; import javax.xml.transform.sax.TransformerHandler; import javax.xml.transform.stream.StreamResult; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.mylyn.internal.tasks.core.XmlReaderUtil; import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector; import org.eclipse.mylyn.tasks.core.IRepositoryManager; import org.eclipse.mylyn.tasks.core.TaskRepository; import org.eclipse.mylyn.tasks.core.data.AbstractTaskDataHandler; import org.eclipse.mylyn.tasks.core.data.ITaskDataWorkingCopy; import org.eclipse.mylyn.tasks.core.data.TaskData; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; /** * @author Steffen Pingel */ public class TaskDataExternalizer { /** * Replaces the first 38 bytes of a an input stream with an XML 1.1 header. */ public static class Xml11InputStream extends FilterInputStream { /** * XML 1.1 header. */ byte[] header; /** * Current position in {@link #header}. */ int pointer; public Xml11InputStream(InputStream in) throws IOException { super(in); header = new String("<?xml version=\"1.1\" encoding=\"UTF-8\"?>").getBytes("US-ASCII"); //$NON-NLS-1$ //$NON-NLS-2$ } @Override public synchronized void reset() throws IOException { throw new IOException(); } @Override public synchronized void mark(int readlimit) { } @Override public boolean markSupported() { return false; } @Override public int read(byte[] b, int off, int len) throws IOException { // fill b with bytes from header until the header has been read if (pointer < header.length) { int read = 0; for (; pointer < header.length && read < len; read++, pointer++) { b[off + read] = header[pointer]; readByte(); } return read; } else { return super.read(b, off, len); } } /** * Advances the underlying stream by a single byte. */ private void readByte() throws IOException, EOFException { if (in.read() == -1) { throw new EOFException(); } } @Override public int read() throws IOException { if (pointer < header.length) { readByte(); return header[pointer++]; } else { return super.read(); } } @Override public long skip(long n) throws IOException { if (pointer < header.length) { // skip at most the number of bytes remaining in the header long skip = Math.min(header.length - pointer, n); // never skip more bytes than underlying stream skip = super.skip(skip); pointer += skip; return skip; } else { return super.skip(n); } } } private final IRepositoryManager taskRepositoryManager; public TaskDataExternalizer(IRepositoryManager taskRepositoryManager) { this.taskRepositoryManager = taskRepositoryManager; } private void migrate(final TaskDataState taskDataState) throws IOException { // for testing if (taskRepositoryManager == null) { return; } String connectorKind = taskDataState.getConnectorKind(); AbstractRepositoryConnector connector = taskRepositoryManager.getRepositoryConnector(connectorKind); if (connector == null) { throw new IOException("No repository connector for kind \"" + connectorKind + "\" found"); //$NON-NLS-1$ //$NON-NLS-2$ } String repositoryUrl = taskDataState.getRepositoryUrl(); final TaskRepository taskRepository = taskRepositoryManager.getRepository(connectorKind, repositoryUrl); if (taskRepository == null) { throw new IOException("Repository \"" + repositoryUrl + "\" not found for kind \"" + connectorKind + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } final AbstractTaskDataHandler taskDataHandler = connector.getTaskDataHandler(); if (taskDataHandler != null) { migrate(taskDataState.getLastReadData(), taskRepository, taskDataHandler); migrate(taskDataState.getRepositoryData(), taskRepository, taskDataHandler); migrate(taskDataState.getEditsData(), taskRepository, taskDataHandler); } } private void migrate(final TaskData taskData, final TaskRepository taskRepository, final AbstractTaskDataHandler taskDataHandler) { if (taskData != null) { SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { // ignore } public void run() throws Exception { taskDataHandler.migrateTaskData(taskRepository, taskData); } }); } } public TaskDataState readState(InputStream in) throws IOException, SAXException { XMLReader parser = XmlReaderUtil.createXmlReader(); TaskDataStateReader handler = new TaskDataStateReader(taskRepositoryManager); parser.setContentHandler(handler); parser.parse(new InputSource(in)); TaskDataState taskDataState = handler.getTaskDataState(); if (taskDataState != null) { migrate(taskDataState); } return taskDataState; } public void writeState(OutputStream out, ITaskDataWorkingCopy state) throws IOException { try { SAXTransformerFactory transformerFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); TransformerHandler handler = transformerFactory.newTransformerHandler(); Transformer serializer = handler.getTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ serializer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ handler.setResult(new StreamResult(out)); TaskDataStateWriter writer = new TaskDataStateWriter(handler); writer.write(state); } catch (TransformerException e) { throw new IOException("Error writing task data" + e.getMessageAndLocation()); //$NON-NLS-1$ } catch (SAXException e) { throw new IOException("Error writing task data" + e.getMessage()); //$NON-NLS-1$ } } }