Java tutorial
/******************************************************************************* * 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; } }