com.google.gwt.eclipse.core.speedtracer.SymbolManifestGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gwt.eclipse.core.speedtracer.SymbolManifestGenerator.java

Source

/*******************************************************************************
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * 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
 *
 * 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 com.google.gwt.eclipse.core.speedtracer;

import com.google.gdt.eclipse.core.ResourceUtils;
import com.google.gdt.eclipse.platform.jetty.JSON;
import com.google.gwt.eclipse.core.GWTPluginLog;

import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * Generates the symbol manifest file required by Speed Tracer to show Java
 * stack traces and jump to the IDE. This will also copy the required symbol
 * maps into the WAR folder.
 */
public class SymbolManifestGenerator {

    /**
     * Models a .cache.html or .cache.js file generated by GWT.
     */
    private static class Resource {
        public final String rootRelativePath;
        public final String hash;
        public final String moduleName;

        public Resource(String rootRelativePath, String hash, String moduleName) {
            this.rootRelativePath = rootRelativePath;
            this.hash = hash;
            this.moduleName = moduleName;
        }
    }

    static final String PROJECT_KEY = "_paramProject";

    private static final String SYMBOL_MANIFEST_FILENAME = "symbolmanifest.json";

    private static final String SYMBOLS_FOLDER_NAME = "symbolsforspeedtracer";

    private static final FilenameFilter RESOURCE_NAME_FILTER = new FilenameFilter() {
        public boolean accept(File dir, String name) {
            return ResourceUtils.endsWith(name, ".cache.html") || ResourceUtils.endsWith(name, ".cache.js");
        }
    };

    /**
     * @param warOutFolder the WAR out folder
     * @return the symbol manifest location as a {@link File} object (no existence
     *         checks are done.)
     */
    public static File getSymbolManifestFile(File warOutFolder) {
        return new File(warOutFolder, SYMBOL_MANIFEST_FILENAME);
    }

    public static File getSymbolMapsFolder(File warOutFolder) {
        return new File(warOutFolder, SYMBOLS_FOLDER_NAME);
    }

    private final List<String> moduleNames;

    private final File extraFolder;

    private final String projectName;

    private final File warOutFolder;

    /**
     * @param warOutFolder the WAR output directory (also assumed to be the folder
     *          holding a directory for each module's compiled output)
     * @param moduleNames the name of the modules to include in the manifest
     * @param extraFolder the folder given to the GWT compiler for storing extra
     *          files from the compile
     */
    public SymbolManifestGenerator(File warOutFolder, List<String> moduleNames, File extraFolder,
            String projectName) {
        this.warOutFolder = warOutFolder;
        this.moduleNames = moduleNames;
        this.extraFolder = extraFolder;
        this.projectName = projectName;
    }

    /**
     * @return the contents of the symbol manifest file
     * @throws OperationCanceledException
     */
    public String generate() throws OperationCanceledException {
        // TreeMap so generated JSON is easier to read since ordering is maintained
        Map<String, Object> rootJsonMap = new TreeMap<String, Object>();

        List<Resource> resources = collectResources();
        for (Resource resource : resources) {
            Map<String, Object> resourceJsonMap = new TreeMap<String, Object>();
            rootJsonMap.put(resource.rootRelativePath, resourceJsonMap);

            addSymbolsForResourceToResourceJsonMap(resource, resourceJsonMap);
            resourceJsonMap.put("type", "gwt");

            Map<String, Object> sourceViewerServerMap = new TreeMap<String, Object>();
            resourceJsonMap.put("sourceViewerServer", sourceViewerServerMap);
            sourceViewerServerMap.put("url", SourceViewerServer.INSTANCE.getViewSourceUrl());
            sourceViewerServerMap.put(PROJECT_KEY, projectName);
        }

        return JSON.toString(rootJsonMap);
    }

    private void addSymbolsForResourceToResourceJsonMap(Resource resource, Map<String, Object> resourceJsonMap) {
        File symbolMapFile = findValidSymbolMapFile(resource);
        if (symbolMapFile == null) {
            GWTPluginLog.logWarning("Could not find a symbol map file for " + resource.rootRelativePath);
            return;
        }

        Path destSymbolMapRelativePath = new Path(
                SYMBOLS_FOLDER_NAME + "/" + resource.moduleName + "/" + resource.hash + ".symbolMap");
        File destSymbolMapFile = new File(warOutFolder, destSymbolMapRelativePath.toOSString());
        try {
            ResourceUtils.copyFile(symbolMapFile, destSymbolMapFile);
        } catch (IOException e) {
            GWTPluginLog.logError(e, "Could not copy the symbol map from " + symbolMapFile.getPath() + " to "
                    + destSymbolMapFile.getPath());
            return;
        }

        resourceJsonMap.put("symbols", destSymbolMapRelativePath.toString());
    }

    private File[] collectResourceFilesForModule(File moduleFolder) {
        return moduleFolder.listFiles(RESOURCE_NAME_FILTER);
    }

    private List<Resource> collectResources() {
        List<Resource> resources = new ArrayList<Resource>();

        for (String moduleName : moduleNames) {
            File moduleFolder = new File(warOutFolder, moduleName);
            if (!moduleFolder.exists()) {
                continue;
            }

            for (File resourceFile : collectResourceFilesForModule(moduleFolder)) {
                String resourceName = resourceFile.getName();
                // The hash used by GWT is 32 characters
                if (resourceName.length() < 32) {
                    continue;
                }

                String rootRelativePath = moduleName + "/" + resourceName;
                String hash = resourceName.substring(0, 32);
                resources.add(new Resource(rootRelativePath, hash, moduleName));
            }
        }

        return resources;
    }

    private File findValidSymbolMapFile(Resource resource) {
        /*
         * TODO: require less knowledge of the layout of the "extra" folder. One way
         * is to move a lot of this logic into GWT SDK.
         */
        File symbolMapFile = new File(extraFolder,
                new Path(resource.moduleName + "/symbolMaps/" + resource.hash + ".symbolMap").toOSString());
        return symbolMapFile.exists() ? symbolMapFile : null;
    }

}