/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.dev.util; import com.google.gwt.dev.util.NullOutputFileSet.NullOutputStream; import com.google.gwt.dev.util.collect.HashSet; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Set; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * An {@link OutputFileSet} on a jar file. */ public class OutputFileSetOnJar extends OutputFileSet { /** * An output stream on a jar entry for <code>jar</code>. It is assumed that * the entry has already been written, so this class only has to forward the * writes. */ private final class OutputStreamOnJarEntry extends OutputStream { @Override public void close() throws IOException { jar.closeEntry(); } @Override public void write(byte b[], int off, int len) throws IOException { jar.write(b, off, len); } @Override public void write(int b) throws IOException { jar.write(b); } } public static final boolean normalizeTimestamps = Boolean.parseBoolean( System.getProperty("gwt.normalizeTimestamps", "false")); /** * Returns the parent path of forward-slash based partial path. Assumes the * given path does not end with a trailing slash. */ private static String getParentPath(String path) { assert !path.endsWith("/"); int pos = path.lastIndexOf('/'); return (pos >= 0) ? path.substring(0, pos) : null; } private Set<String> createdDirs = new HashSet<String>(); private final JarOutputStream jar; private final String pathPrefix; private final Set<String> seenEntries = new HashSet<String>(); public OutputFileSetOnJar(File jarFile, String pathPrefix) throws IOException { super(jarFile.getAbsolutePath()); jarFile.delete(); jar = new JarOutputStream(new FileOutputStream(jarFile)); this.pathPrefix = pathPrefix; } @Override public void close() throws IOException { jar.close(); } @Override public OutputStream createNewOutputStream(String path, long lastModifiedTime) throws IOException { String fullPath = pathPrefix + path; if (seenEntries.contains(fullPath)) { return new NullOutputStream(); } seenEntries.add(fullPath); mkzipDirs(getParentPath(fullPath)); ZipEntry zipEntry = new ZipEntry(fullPath); if (normalizeTimestamps) { zipEntry.setTime(0); } else if (lastModifiedTime >= 0) { zipEntry.setTime(lastModifiedTime); } jar.putNextEntry(zipEntry); return new OutputStreamOnJarEntry(); } /** * Creates directory entries within a zip archive. Uses * <code>createdDirs</code> to avoid creating entries for the same path twice. * * @param path the path of a directory within the archive to create */ private void mkzipDirs(String path) throws IOException { if (path == null) { return; } if (createdDirs.contains(path)) { return; } mkzipDirs(getParentPath(path)); ZipEntry entry = new ZipEntry(path + '/'); entry.setSize(0); entry.setCompressedSize(0); entry.setCrc(0); entry.setMethod(ZipOutputStream.STORED); if (normalizeTimestamps) { entry.setTime(0); } jar.putNextEntry(entry); createdDirs.add(path); } }