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