/* Copyright (C) 2010 Mobile Sorcery AB This program is free software; you can redistribute it and/or modify it under the terms of the Eclipse Public License v1.0. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for more details. You should have received a copy of the Eclipse Public License v1.0 along with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html */ package com.mobilesorcery.sdk.profiling; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import com.mobilesorcery.sdk.core.ISLDInfo; import com.mobilesorcery.sdk.core.ParseException; import com.mobilesorcery.sdk.core.SLD; import com.mobilesorcery.sdk.core.SectionedPropertiesFile; import com.mobilesorcery.sdk.core.Util; import com.mobilesorcery.sdk.core.SectionedPropertiesFile.Section; import com.mobilesorcery.sdk.core.SectionedPropertiesFile.Section.Entry; import com.mobilesorcery.sdk.profiling.emulator.ProfilingSession; import com.mobilesorcery.sdk.profiling.internal.ProfilingDataParser; public class ProfilingSessionParser { public class PathMappingLocationProvider implements ILocationProvider { private Map<IPath, IPath> pathMapping = new HashMap<IPath, IPath>(); private IWorkspaceRoot wsRoot; public PathMappingLocationProvider(Map<String, String> pathMapping) { wsRoot = ResourcesPlugin.getWorkspace().getRoot(); for (Map.Entry<String, String> pathMappingEntry : pathMapping.entrySet()) { this.pathMapping.put(new Path(pathMappingEntry.getKey()), new Path(pathMappingEntry.getValue())); } } public IFile getLocation(Object element) { if (element instanceof String) { element = new Path((String) element); } if (element instanceof IPath) { IPath workspaceRelativePath = pathMapping.get(element); return wsRoot.getFile(workspaceRelativePath); } return null; } } private static final String PATH_MAPPING_SECTION = "PathMapping"; private static final String NAME_PROP = "name"; private static final String START_TIME_PROP = "startTime"; public IProfilingSession parse(File input) throws IOException, ParseException { FileInputStream inputStream = new FileInputStream(input); try { return parse(inputStream); } finally { Util.safeClose(inputStream); } } public IProfilingSession parse(InputStream input) throws IOException, ParseException { ZipInputStream zipInput = new ZipInputStream(input); byte[] profilingDataBuffer = null; ISLDInfo sld = null; SectionedPropertiesFile properties = null; for (ZipEntry entry = zipInput.getNextEntry(); entry != null; entry = zipInput.getNextEntry()) { String entryName = entry.getName(); if (entryName.endsWith("fp.xml")) { profilingDataBuffer = readEntireStream(zipInput); } else if (entryName.endsWith("sld.tab")) { sld = SLD.parseSLDInfo(zipInput, null); } else if (entryName.endsWith("manifest.mf")) { properties = parseMetaData(zipInput); } zipInput.closeEntry(); } if (sld == null || profilingDataBuffer == null || properties == null) { throw new IOException("Invalid profile session data -- no SLD/profiling data/metadata"); } IInvocation invocation = parseProfilingData(new ByteArrayInputStream(profilingDataBuffer), sld); return constructSession(properties, invocation); } public void unparse(IProfilingSession session, File output) throws IOException { OutputStream writer = new FileOutputStream(output); try { unparse(session, writer); } finally { Util.safeClose(writer); } } public void unparse(List<IProfilingSession> sessions, File output) throws IOException { if (sessions.size() != 1) { throw new IOException("Current only supports saving ONE session"); } unparse(sessions.get(0), output); } public void unparse(IProfilingSession session, OutputStream output) throws IOException { SLD sld = (SLD) session.getAdapter(SLD.class); ISLDInfo info = sld == null ? null : sld.parseSLD(); if (info == null) { throw new IOException("No SLD information available -- cannot save session"); } File fpFile = session.getProfilingFile(); if (fpFile == null || !fpFile.exists()) { throw new IOException("No profiling information available -- cannot save session"); } ZipOutputStream zipOutput = new ZipOutputStream(output); ZipEntry sldEntry = new ZipEntry("/sld.tab"); zipOutput.putNextEntry(sldEntry); File sldFile = info.getSLDFile(); dumpEntireFile(sldFile, zipOutput); ZipEntry fpEntry = new ZipEntry("/fp.xml"); zipOutput.putNextEntry(fpEntry); dumpEntireFile(fpFile, zipOutput); writeMetaData(session, info, zipOutput); zipOutput.close(); } private void writeMetaData(IProfilingSession session, ISLDInfo info, ZipOutputStream zipOutput) throws IOException { SectionedPropertiesFile properties = SectionedPropertiesFile.create(); properties.getDefaultSection().addEntry(new Entry(NAME_PROP, session.getName())); properties.getDefaultSection().addEntry(new Entry(START_TIME_PROP, Long.toString(session.getStartTime().getTimeInMillis()))); Section pathMapping = properties.addSection(PATH_MAPPING_SECTION); for (String filename : info.getAllFilenames()) { IFile file = session.getLocationProvider().getLocation(new File(filename)); if (file != null) { // Map filename to workspace-relative path pathMapping.addEntry(filename, file.getFullPath().toPortableString()); } } ZipEntry propsEntry = new ZipEntry("manifest.mf"); zipOutput.putNextEntry(propsEntry); zipOutput.write(properties.toString().getBytes("UTF-8")); zipOutput.closeEntry(); } private void dumpEntireFile(File file, ZipOutputStream zipOutput) throws IOException { FileInputStream input = new FileInputStream(file); try { zipOutput.write(readEntireStream(input)); zipOutput.closeEntry(); } finally { Util.safeClose(input); } } private ProfilingSession constructSession(SectionedPropertiesFile properties, IInvocation invocation) { Map<String, String> profilingProps = properties.getDefaultSection().getEntriesAsMap(); String name = profilingProps.get(NAME_PROP); String startTimeStr = profilingProps.get(START_TIME_PROP); Calendar startTime = Calendar.getInstance(); if (startTimeStr != null) { try { startTime.setTimeInMillis(Long.parseLong(startTimeStr)); } catch (NumberFormatException e) { // Just ignore. } } ILocationProvider locationProvider = parsePathMapping(properties.getFirstSection(PATH_MAPPING_SECTION)); ProfilingSession session = new ProfilingSession(name, startTime); if (locationProvider != null) { session.setLocationProvider(locationProvider); } session.setInvocation(invocation); return session; } private ILocationProvider parsePathMapping(Section pathMappingSection) { if (pathMappingSection != null) { return new PathMappingLocationProvider(pathMappingSection.getEntriesAsMap()); } return null; } private SectionedPropertiesFile parseMetaData(InputStream input) throws IOException { SectionedPropertiesFile properties = SectionedPropertiesFile.parse(new InputStreamReader(input)); return properties; } private byte[] readEntireStream(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[65536]; for (int read = input.read(buffer); read != -1; read = input.read(buffer)) { output.write(buffer, 0, read); } return output.toByteArray(); } private IInvocation parseProfilingData(InputStream input, ISLDInfo sld) throws IOException, ParseException { ProfilingDataParser parser = new ProfilingDataParser(); return parser.parse(input, sld); } }