0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.java.source.parsing;
0043:
0044: import java.io.BufferedInputStream;
0045: import java.io.ByteArrayInputStream;
0046: import java.io.ByteArrayOutputStream;
0047: import java.io.File;
0048: import java.io.FileInputStream;
0049: import java.io.FileOutputStream;
0050: import java.io.IOException;
0051: import java.io.InputStream;
0052: import java.io.InputStreamReader;
0053: import java.io.OutputStream;
0054: import java.io.OutputStreamWriter;
0055: import java.io.Reader;
0056: import java.io.StringReader;
0057: import java.io.UnsupportedEncodingException;
0058: import java.io.Writer;
0059: import java.net.MalformedURLException;
0060: import java.net.URI;
0061: import java.net.URISyntaxException;
0062: import java.net.URL;
0063: import java.net.URLEncoder;
0064: import java.nio.CharBuffer;
0065: import java.nio.charset.Charset;
0066: import java.util.Comparator;
0067: import java.util.zip.ZipEntry;
0068: import java.util.zip.ZipFile;
0069: import javax.lang.model.element.Modifier;
0070: import javax.lang.model.element.NestingKind;
0071: import javax.tools.JavaFileObject;
0072: import org.netbeans.api.queries.FileEncodingQuery;
0073: import org.netbeans.modules.java.ClassDataLoader;
0074: import org.netbeans.modules.java.JavaDataLoader;
0075: import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation;
0076: import org.netbeans.modules.java.source.JavaFileFilterQuery;
0077: import org.openide.filesystems.FileObject;
0078: import org.openide.filesystems.FileSystem;
0079: import org.openide.filesystems.FileUtil;
0080: import org.openide.filesystems.FileUtil;
0081: import org.openide.filesystems.Repository;
0082: import org.openide.loaders.DataFolder;
0083: import org.openide.loaders.DataObject;
0084: import org.openide.util.NbBundle;
0085: import org.openide.util.Utilities;
0086:
0087: /** Creates various kinds of file objects
0088: *
0089: * XXX - Rename to JavaFileObjects
0090: *
0091: * @author Petr Hrebejk
0092: */
0093: public class FileObjects {
0094:
0095: public static final Comparator<String> SIMPLE_NAME_STRING_COMPARATOR = new SimpleNameStringComparator();
0096: public static final Comparator<JavaFileObject> SIMPLE_NAME_FILEOBJECT_COMPARATOR = new SimpleNameFileObjectComparator();
0097:
0098: public static final String JAVA = JavaDataLoader.JAVA_EXTENSION;
0099: public static final String CLASS = ClassDataLoader.CLASS_EXTENSION;
0100: public static final String JAR = "jar"; //NOI18N
0101: public static final String FILE = "file"; //NOI18N
0102: public static final String ZIP = "zip"; //NOI18N
0103: public static final String HTML = "html"; //NOI18N
0104: public static final String SIG = "sig"; //NOI18N
0105: public static final String RS = "rs"; //NOI18N
0106:
0107: /** Creates a new instance of FileObjects */
0108: private FileObjects() {
0109: }
0110:
0111: // Public methods ----------------------------------------------------------
0112:
0113: /**
0114: * Creates {@link JavaFileObject} for a file inside an archive file. The archive file
0115: * is opened every time an input stream of this {@link JavaFileObject} is needed, it may
0116: * slow down the javac attribution.
0117: * @param zip an archive file
0118: * @param folder in the archive
0119: * @param name the base (simple name)
0120: * @return {@link JavaFileObject}, never returns null
0121: */
0122: public static JavaFileObject zipFileObject(File zipFile,
0123: String folder, String baseName, long mtime) {
0124: assert zipFile != null;
0125: return new ZipFileObject(zipFile, folder, baseName, mtime);
0126: }
0127:
0128: /**
0129: * Creates {@link JavaFileObject} for a file inside an archive file. The returned {@link JavaFileObject}
0130: * tries to use {@link RandomAccessFile} to read the archive entry, in the case when it's not able to
0131: * find the entry (unsupported zip file format) it delegates into the {@link ZipFile}.
0132: * @param zip an archive file
0133: * @param folder in the archive
0134: * @param name the base (simple name)
0135: * @param offset the start of zip entry in the zip file
0136: * @return {@link JavaFileObject}, never returns null
0137: */
0138: public static JavaFileObject zipFileObject(File zipFile,
0139: String folder, String baseName, long mtime, long offset) {
0140: assert zipFile != null;
0141: return new FastZipFileObject(zipFile, folder, baseName, mtime,
0142: offset);
0143: }
0144:
0145: /**
0146: * Creates {@link JavaFileObject} for a file inside an {@link ZipFile}. The returned {@link JavaFileObject}
0147: * uses an opened ZipFile. It's a fastes way to read the archive file content, but the opened {@link ZipFile}s
0148: * cannot be modified. So, this {@link JavaFileObject}s can be used only for platform classpath.
0149: * @param zip an archive file
0150: * @param folder in the archive
0151: * @param name the base (simple name)
0152: * @param offset the start of zip entry in the zip file
0153: * @return {@link JavaFileObject}, never returns null
0154: */
0155: public static JavaFileObject zipFileObject(ZipFile zipFile,
0156: String folder, String baseName, long mtime) {
0157: assert zipFile != null;
0158: return new CachedZipFileObject(zipFile, folder, baseName, mtime);
0159: }
0160:
0161: /**
0162: * Creates {@link JavaFileObject} for a regular {@link File}
0163: * @param file for which the {@link JavaFileObject} should be created
0164: * @pram root - the classpath root owning the file
0165: * @return {@link JavaFileObject}, never returns null
0166: */
0167: public static JavaFileObject fileFileObject(final File file,
0168: final File root, final JavaFileFilterImplementation filter) {
0169: return fileFileObject(file, root, filter, null);
0170: }
0171:
0172: /**
0173: * Creates {@link JavaFileObject} for a regular {@link File}
0174: * @param file for which the {@link JavaFileObject} should be created
0175: * @param root - the classpath root owning the file
0176: * @param encoding - the file's encoding
0177: * @return {@link JavaFileObject}, never returns null
0178: */
0179: public static JavaFileObject fileFileObject(final File file,
0180: final File root, final JavaFileFilterImplementation filter,
0181: Charset encoding) {
0182: assert file != null;
0183: assert root != null;
0184: String[] pkgNamePair = getFolderAndBaseName(getRelativePath(
0185: root, file), File.separatorChar);
0186: return new RegularFileObject(file, convertFolder2Package(
0187: pkgNamePair[0], File.separatorChar), pkgNamePair[1],
0188: filter, encoding);
0189: }
0190:
0191: public static JavaFileObject templateFileObject(
0192: final FileObject root, final String path, String name) {
0193: assert root != null;
0194: assert path != null;
0195: JavaFileFilterImplementation filter = JavaFileFilterQuery
0196: .getFilter(root);
0197: Charset encoding = FileEncodingQuery.getEncoding(root);
0198: File rootFile = FileUtil.toFile(root);
0199: if (rootFile == null) {
0200: throw new IllegalArgumentException();
0201: }
0202: File file = FileUtil.normalizeFile(new File(new File(rootFile,
0203: path.replace('/', File.separatorChar)), name)); //NOI18N
0204: return new NewFromTemplateFileObject(file,
0205: convertFolder2Package(path), name, filter, encoding);
0206: }
0207:
0208: /**
0209: * Creates {@link JavaFileObject} for a NetBeans {@link FileObject}
0210: * Any client which needs to create {@link JavaFileObject} for java
0211: * source file should use this factory method.
0212: * @param {@link FileObject} for which the {@link JavaFileObject} should be created
0213: * @param {@link FileObject} root owning the file
0214: * @return {@link JavaFileObject}, never returns null
0215: * @exception {@link IOException} may be thrown
0216: */
0217: public static SourceFileObject nbFileObject(final FileObject file,
0218: final FileObject root) throws IOException {
0219: return nbFileObject(file, root, null, false);
0220: }
0221:
0222: /**
0223: * Creates {@link JavaFileObject} for a NetBeans {@link FileObject}
0224: * Any client which needs to create {@link JavaFileObject} for java
0225: * source file should use this factory method.
0226: * @param {@link FileObject} for which the {@link JavaFileObject} should be created
0227: * @param {@link FileObject} root owning the file
0228: * @param renderNow if true the snap shot of the file is taken immediately
0229: * @return {@link JavaFileObject}, never returns null
0230: * @exception {@link IOException} may be thrown
0231: */
0232: public static SourceFileObject nbFileObject(final FileObject file,
0233: final FileObject root, JavaFileFilterImplementation filter,
0234: boolean renderNow) throws IOException {
0235: assert file != null;
0236: if (!file.isValid() || file.isVirtual()) {
0237: throw new InvalidFileException(file);
0238: }
0239: return new SourceFileObject(file, root, filter, renderNow);
0240: }
0241:
0242: /**
0243: * Creates virtual {@link JavaFileObject} with given name and content.
0244: * This method should be used only by tests, regular client should never
0245: * use this method.
0246: * @param content the content of the {@link JavaFileObject}
0247: * @param name the name of the {@link JavaFileObject}
0248: * @return {@link JavaFileObject}, never returns null
0249: */
0250: public static JavaFileObject memoryFileObject(CharSequence content,
0251: CharSequence name) {
0252: final String nameStr = name.toString();
0253: if (!nameStr.equals(getBaseName(nameStr))) {
0254: throw new IllegalArgumentException("Memory is flat"); //NOI18N
0255: }
0256: int length = content.length();
0257: if (length != 0
0258: && Character.isWhitespace(content.charAt(length - 1))) {
0259: return new MemoryFileObject(nameStr, CharBuffer
0260: .wrap(content));
0261: } else {
0262: return new MemoryFileObject(nameStr,
0263: (CharBuffer) CharBuffer.allocate(length + 1)
0264: .append(content).append(' ').flip());
0265: }
0266:
0267: }
0268:
0269: public static String stripExtension(String fileName) {
0270: int dot = fileName.lastIndexOf(".");
0271: return (dot == -1 ? fileName : fileName.substring(0, dot));
0272: }
0273:
0274: public static String getExtension(final String fileName) {
0275: int dot = fileName.lastIndexOf('.');
0276: return (dot == -1 || dot == fileName.length() - 1) ? ""
0277: : fileName.substring(dot + 1); //NOI18N
0278: }
0279:
0280: /**
0281: * Returns the name of JavaFileObject, similar to
0282: * {@link java.io.File#getName}
0283: */
0284: public static String getName(final JavaFileObject fo,
0285: final boolean noExt) {
0286: assert fo != null;
0287: if (fo instanceof Base) {
0288: Base baseFileObject = (Base) fo;
0289: if (noExt) {
0290: return baseFileObject.getName();
0291: } else {
0292: StringBuilder sb = new StringBuilder();
0293: sb.append(baseFileObject.getName());
0294: sb.append('.'); //NOI18N
0295: sb.append(baseFileObject.getExt());
0296: return sb.toString();
0297: }
0298: }
0299: try {
0300: final URL url = fo.toUri().toURL();
0301: String path = url.getPath();
0302: int index1 = path.lastIndexOf('/');
0303: int len;
0304: if (noExt) {
0305: final int index2 = path.lastIndexOf('.');
0306: if (index2 > index1) {
0307: len = index2;
0308: } else {
0309: len = path.length();
0310: }
0311: } else {
0312: len = path.length();
0313: }
0314: path = path.substring(index1 + 1, len);
0315: return path;
0316: } catch (MalformedURLException e) {
0317: return null;
0318: }
0319: }
0320:
0321: /**
0322: * Returns the basename name without folder path
0323: * @param file name, eg. obtained from {@link FileObjects#getPath} or {java.io.File.getPath}
0324: * @return the base name
0325: * @see #getBaseName(String,char)
0326: */
0327: public static String getBaseName(String fileName) {
0328: return getBaseName(fileName, File.separatorChar);
0329: }
0330:
0331: /**
0332: * Returns the basename name without folder path. You can specify
0333: * the path separator since eg zip files uses '/' regardless of platform.
0334: * @param file name, eg. obtained from {@link FileObjects#getPath} or {java.io.File.getPath}
0335: * @param separator path separator
0336: * @return the base name
0337: */
0338: public static String getBaseName(String fileName, char separator) {
0339: return getFolderAndBaseName(fileName, separator)[1];
0340: }
0341:
0342: /**
0343: *Returns the folder (package name separated by original separators)
0344: *and base name.
0345: * @param path
0346: * @return array of 2 strings, 1st the folder 2nd the base name
0347: */
0348: public static String[] getFolderAndBaseName(final String fileName,
0349: final char separator) {
0350: final int i = fileName.lastIndexOf(separator);
0351: if (i == -1) {
0352: return new String[] { "", fileName }; //NOI18N
0353: } else {
0354: return new String[] { fileName.substring(0, i),
0355: fileName.substring(i + 1) };
0356: }
0357: }
0358:
0359: public static String getBinaryName(final File file, final File root) {
0360: assert file != null && root != null;
0361: String fileName = FileObjects.getRelativePath(root, file);
0362: int index = fileName.lastIndexOf('.'); //NOI18N
0363: if (index > 0) {
0364: fileName = fileName.substring(0, index);
0365: }
0366: return fileName.replace(File.separatorChar, '.'); //NOI18N
0367: }
0368:
0369: public static String getSimpleName(JavaFileObject fo) {
0370:
0371: String name = getName(fo, true);
0372: int i = name.lastIndexOf('$');
0373: if (i == -1) {
0374: return name;
0375: } else {
0376: return name.substring(i + 1);
0377: }
0378: }
0379:
0380: public static String getSimpleName(String fileName) {
0381:
0382: String name = getBaseName(fileName);
0383:
0384: int i = name.lastIndexOf('$');
0385: if (i == -1) {
0386: return name;
0387: } else {
0388: return name.substring(i + 1);
0389: }
0390:
0391: }
0392:
0393: public static String convertPackage2Folder(String packageName) {
0394: return packageName.replace('.', '/');
0395: }
0396:
0397: public static String convertFolder2Package(String packageName) {
0398: return convertFolder2Package(packageName, '/'); //NOI18N
0399: }
0400:
0401: public static String convertFolder2Package(String packageName,
0402: char folderSeparator) {
0403: return packageName.replace(folderSeparator, '.');
0404: }
0405:
0406: public static String getRelativePath(final String packageName,
0407: final String relativeName) {
0408: StringBuilder relativePath = new StringBuilder();
0409: relativePath.append(packageName.replace('.', '/'));
0410: relativePath.append(relativeName);
0411: return relativePath.toString();
0412: }
0413:
0414: public static String[] getParentRelativePathAndName(
0415: final String className) {
0416: if (className.charAt(className.length() - 1) == '.') {
0417: return null;
0418: }
0419: final int index = className.lastIndexOf('.');
0420: if (index < 0) {
0421: return new String[] { "", //NOI18N
0422: className };
0423: } else {
0424: return new String[] {
0425: className.substring(0, index).replace('.', '/'), //NOI18N
0426: className.substring(index + 1) };
0427: }
0428: }
0429:
0430: /**
0431: * Determines {@link JavaFileObject.Kind} for given extension
0432: * @param extension
0433: * @return the found kind
0434: */
0435: public static JavaFileObject.Kind getKind(final String extension) {
0436: if (extension == null) {
0437: return JavaFileObject.Kind.OTHER;
0438: }
0439: String lcextension = extension.toLowerCase();
0440: if (FileObjects.JAVA.equals(lcextension)) {
0441: return JavaFileObject.Kind.SOURCE;
0442: }
0443: if (FileObjects.CLASS.equals(lcextension)
0444: || FileObjects.SIG.equals(lcextension)) {
0445: return JavaFileObject.Kind.CLASS;
0446: }
0447: if (FileObjects.HTML.equals(lcextension)) {
0448: return JavaFileObject.Kind.HTML;
0449: }
0450: return JavaFileObject.Kind.OTHER;
0451: }
0452:
0453: public static void deleteRecursively(final File folder) {
0454: assert folder != null;
0455: if (folder.isDirectory()) {
0456: File[] children = folder.listFiles();
0457: if (children != null) {
0458: for (File file : children) {
0459: deleteRecursively(file);
0460: }
0461: }
0462: }
0463: folder.delete();
0464: }
0465:
0466: // Private methods ---------------------------------------------------------
0467:
0468: // Innerclasses ------------------------------------------------------------
0469:
0470: public static abstract class Base implements JavaFileObject {
0471:
0472: protected final JavaFileObject.Kind kind;
0473: protected final String pkgName;
0474: protected final String nameWithoutExt;
0475: protected final String ext;
0476:
0477: protected Base(final String pkgName, final String name) {
0478: assert pkgName != null;
0479: assert name != null;
0480: this .pkgName = pkgName;
0481: String[] res = getNameExtPair(name);
0482: this .nameWithoutExt = res[0];
0483: this .ext = res[1];
0484: this .kind = FileObjects.getKind(this .ext);
0485: }
0486:
0487: public JavaFileObject.Kind getKind() {
0488: return this .kind;
0489: }
0490:
0491: public boolean isNameCompatible(String simplename,
0492: JavaFileObject.Kind k) {
0493: if (this .kind != k) {
0494: return false;
0495: }
0496: return nameWithoutExt.equals(simplename);
0497: }
0498:
0499: public NestingKind getNestingKind() {
0500: return null;
0501: }
0502:
0503: public Modifier getAccessLevel() {
0504: return null;
0505: }
0506:
0507: @Override
0508: public String toString() {
0509: return this .toUri().toString();
0510: }
0511:
0512: public String getPackage() {
0513: return this .pkgName;
0514: }
0515:
0516: public String getNameWithoutExtension() {
0517: return this .nameWithoutExt;
0518: }
0519:
0520: public String getName() {
0521: return this .nameWithoutExt + '.' + ext;
0522: }
0523:
0524: public String getExt() {
0525: return this .ext;
0526: }
0527:
0528: private static String[] getNameExtPair(String name) {
0529: int index = name.lastIndexOf('.');
0530: String namenx;
0531: String ext;
0532: if (index <= 0) {
0533: namenx = name;
0534: ext = ""; //NOI18N
0535: } else {
0536: namenx = name.substring(0, index);
0537: if (index == name.length() - 1) {
0538: ext = "";
0539: } else {
0540: ext = name.substring(index + 1);
0541: }
0542: }
0543: return new String[] { namenx, ext };
0544: }
0545: }
0546:
0547: public static abstract class FileBase extends Base {
0548:
0549: protected final File f;
0550:
0551: protected FileBase(final File file, final String pkgName,
0552: final String name) {
0553: super (pkgName, name);
0554: assert file != null;
0555: assert file.equals(FileUtil.normalizeFile(file)) : "File: "
0556: + file.getAbsolutePath() + " Normalized File: "
0557: + FileUtil.normalizeFile(file).getAbsolutePath(); //NOI18N
0558: this .f = file;
0559: }
0560:
0561: public File getFile() {
0562: return this .f;
0563: }
0564: }
0565:
0566: public static class InvalidFileException extends IOException {
0567:
0568: public InvalidFileException() {
0569: super ();
0570: }
0571:
0572: public InvalidFileException(final FileObject fo) {
0573: super (NbBundle.getMessage(FileObjects.class,
0574: "FMT_InvalidFile", FileUtil.getFileDisplayName(fo)));
0575: }
0576: }
0577:
0578: public static String getRelativePath(final File root, final File fo) {
0579: final String rootPath = root.getAbsolutePath();
0580: final String foPath = fo.getAbsolutePath();
0581: assert foPath.startsWith(rootPath) : String.format(
0582: "getRelativePath(%s, %s)", rootPath, foPath);
0583: int index = rootPath.length();
0584: if (rootPath.charAt(index - 1) != File.separatorChar) {
0585: index++;
0586: }
0587: int foIndex = foPath.length();
0588: if (foIndex <= index) {
0589: return ""; //NOI18N
0590: }
0591: return foPath.substring(index);
0592: }
0593:
0594: private static class RegularFileObject extends FileBase {
0595:
0596: private URI uriCache;
0597: private final JavaFileFilterImplementation filter;
0598: private final Charset encoding;
0599:
0600: public RegularFileObject(final File f,
0601: final String packageName, final String baseName,
0602: final JavaFileFilterImplementation filter,
0603: Charset encoding) {
0604: super (f, packageName, baseName);
0605: this .filter = filter;
0606: this .encoding = encoding;
0607: }
0608:
0609: public InputStream openInputStream() throws IOException {
0610: return new BufferedInputStream(new FileInputStream(f));
0611: }
0612:
0613: public Reader openReader(boolean b) throws IOException {
0614: throw new UnsupportedOperationException();
0615: }
0616:
0617: public OutputStream openOutputStream() throws IOException {
0618: return new FileOutputStream(f);
0619: }
0620:
0621: public Writer openWriter() throws IOException {
0622: if (encoding != null) {
0623: return new OutputStreamWriter(new FileOutputStream(f),
0624: encoding);
0625: } else {
0626: return new OutputStreamWriter(new FileOutputStream(f));
0627: }
0628: }
0629:
0630: public @Override
0631: boolean isNameCompatible(String simplename,
0632: JavaFileObject.Kind kind) {
0633: boolean res = super .isNameCompatible(simplename, kind);
0634: if (res) {
0635: return res;
0636: } else if (Utilities.isWindows()) {
0637: return nameWithoutExt.equalsIgnoreCase(simplename);
0638: } else {
0639: return false;
0640: }
0641: }
0642:
0643: public URI toUri() {
0644: if (this .uriCache == null) {
0645: this .uriCache = f.toURI();
0646: }
0647: return this .uriCache;
0648: }
0649:
0650: public long getLastModified() {
0651: return f.lastModified();
0652: }
0653:
0654: public boolean delete() {
0655: return f.delete();
0656: }
0657:
0658: public CharSequence getCharContent(boolean ignoreEncodingErrors)
0659: throws IOException {
0660:
0661: char[] result;
0662: Reader in;
0663:
0664: if (encoding != null) {
0665: in = new InputStreamReader(new FileInputStream(this .f),
0666: encoding);
0667: } else {
0668: in = new InputStreamReader(new FileInputStream(this .f));
0669: }
0670: if (this .filter != null) {
0671: in = this .filter.filterReader(in);
0672: }
0673: int red = 0;
0674: try {
0675: int len = (int) this .f.length();
0676: if (len == 0)
0677: len++; //len - red would be 0 while reading from the stream
0678: result = new char[len + 1];
0679: int rv;
0680: while ((rv = in.read(result, red, len - red)) >= 0) {
0681: red += rv;
0682: //In case the filter enlarged the file
0683: if (red == len) {
0684: char[] _tmp = new char[2 * len];
0685: System.arraycopy(result, 0, _tmp, 0, len);
0686: result = _tmp;
0687: len = result.length;
0688: }
0689: }
0690: } finally {
0691: in.close();
0692: }
0693: result[red++] = '\n'; //NOI18N
0694: CharSequence buffer = CharBuffer.wrap(result, 0, red);
0695: return buffer;
0696: }
0697:
0698: @Override
0699: public boolean equals(Object other) {
0700: if (!(other instanceof RegularFileObject))
0701: return false;
0702: RegularFileObject o = (RegularFileObject) other;
0703: return f.equals(o.f);
0704: }
0705:
0706: @Override
0707: public int hashCode() {
0708: return f.hashCode();
0709: }
0710:
0711: }
0712:
0713: private static class NewFromTemplateFileObject extends
0714: RegularFileObject {
0715:
0716: public NewFromTemplateFileObject(File f, String packageName,
0717: String baseName, JavaFileFilterImplementation filter,
0718: Charset encoding) {
0719: super (f, packageName, baseName, filter, encoding);
0720: }
0721:
0722: @Override
0723: public InputStream openInputStream() throws IOException {
0724: if (f.exists()) {
0725: return super .openInputStream();
0726: }
0727: return new ByteArrayInputStream(new byte[0]);
0728: }
0729:
0730: @Override
0731: public Reader openReader(boolean b) throws IOException {
0732: if (f.exists()) {
0733: return super .openReader(b);
0734: }
0735: return new StringReader(""); //NOI18N
0736: }
0737:
0738: @Override
0739: public OutputStream openOutputStream() throws IOException {
0740: if (!f.exists()) {
0741: create();
0742: }
0743: return super .openOutputStream();
0744: }
0745:
0746: @Override
0747: public Writer openWriter() throws IOException {
0748: if (!f.exists()) {
0749: create();
0750: }
0751: return super .openWriter();
0752: }
0753:
0754: @Override
0755: public CharSequence getCharContent(boolean ignoreEncodingErrors)
0756: throws IOException {
0757: if (f.exists()) {
0758: return super .getCharContent(ignoreEncodingErrors);
0759: }
0760: return ""; //NOI18N
0761: }
0762:
0763: private void create() throws IOException {
0764: File parent = f.getParentFile();
0765: FileObject parentFo = FileUtil.createFolder(parent);
0766: assert parentFo != null;
0767: DataFolder target = DataFolder.findFolder(parentFo);
0768: FileSystem dfs = Repository.getDefault()
0769: .getDefaultFileSystem();
0770: FileObject template = dfs
0771: .findResource("Templates/Classes/Empty.java"); //NOI18N
0772: DataObject templateDobj = DataObject.find(template);
0773: String simpleName = FileObjects.stripExtension(f.getName());
0774: DataObject newDobj = templateDobj.createFromTemplate(
0775: target, simpleName);
0776: assert newDobj != null;
0777: }
0778: }
0779:
0780: /** A subclass of FileObject representing zip entries.
0781: * XXX: What happens when the archive is deleted or rebuilt?
0782: */
0783: public abstract static class ZipFileBase extends Base {
0784:
0785: protected final long mtime;
0786: protected final String resName;
0787:
0788: public ZipFileBase(final String folderName,
0789: final String baseName, long mtime) {
0790: super (convertFolder2Package(folderName), baseName);
0791: this .mtime = mtime;
0792: if (folderName.length() == 0) {
0793: this .resName = baseName;
0794: } else {
0795: StringBuilder resName = new StringBuilder(folderName);
0796: resName.append('/'); //NOI18N
0797: resName.append(baseName);
0798: this .resName = resName.toString();
0799: }
0800: }
0801:
0802: public OutputStream openOutputStream() throws IOException {
0803: throw new UnsupportedOperationException();
0804: }
0805:
0806: public Reader openReader(boolean b) throws IOException {
0807: if (this .getKind() == JavaFileObject.Kind.CLASS) {
0808: throw new UnsupportedOperationException();
0809: } else {
0810: return new InputStreamReader(openInputStream(),
0811: FileObjects.encodingName);
0812: }
0813: }
0814:
0815: public Writer openWriter() throws IOException {
0816: throw new UnsupportedOperationException();
0817: }
0818:
0819: public long getLastModified() {
0820: return mtime;
0821: }
0822:
0823: public boolean delete() {
0824: throw new UnsupportedOperationException();
0825: }
0826:
0827: public CharBuffer getCharContent(boolean ignoreEncodingErrors)
0828: throws IOException {
0829: Reader r = openReader(ignoreEncodingErrors);
0830: try {
0831: int red = 0, rv;
0832:
0833: int len = (int) this .getSize();
0834: char[] result = new char[len + 1];
0835: while ((rv = r.read(result, red, len - red)) > 0
0836: && (red = red + rv) < len)
0837: ;
0838:
0839: int j = 0;
0840: for (int i = 0; i < red; i++) {
0841: if (result[i] == '\r') { //NOI18N
0842: if (i + 1 >= red || result[i + 1] != '\n') { //NOI18N
0843: result[j++] = '\n'; //NOI18N
0844: }
0845: } else {
0846: result[j++] = result[i];
0847: }
0848: }
0849: result[j] = '\n'; //NOI18N
0850: return CharBuffer.wrap(result, 0, j);
0851: } finally {
0852: r.close();
0853: }
0854: }
0855:
0856: public final URI toUri() {
0857: URI zdirURI = this .getArchiveURI();
0858: try {
0859: //Optimistic try and see
0860: return new URI("jar:" + zdirURI.toString() + "!/"
0861: + resName); //NOI18N
0862: } catch (URISyntaxException e) {
0863: //Need to encode the resName part (slower)
0864: final StringBuilder sb = new StringBuilder();
0865: final String[] elements = resName.split("/"); //NOI18N
0866: try {
0867: for (int i = 0; i < elements.length; i++) {
0868: String element = elements[i];
0869: element = URLEncoder.encode(element, "UTF-8"); //NOI18N
0870: element = element.replace("+", "%20"); //NOI18N
0871: sb.append(element);
0872: if (i < elements.length - 1) {
0873: sb.append('/');
0874: }
0875: }
0876: return new URI("jar:" + zdirURI.toString() + "!/"
0877: + sb.toString()); //NOI18N
0878: } catch (final UnsupportedEncodingException e2) {
0879: final IllegalStateException ne = new IllegalStateException();
0880: ne.initCause(e2);
0881: throw ne;
0882: } catch (final URISyntaxException e2) {
0883: final IllegalStateException ne = new IllegalStateException();
0884: ne.initCause(e2);
0885: throw ne;
0886: }
0887: }
0888: }
0889:
0890: @Override
0891: public int hashCode() {
0892: return this .resName.hashCode();
0893: }
0894:
0895: @Override
0896: public boolean equals(Object other) {
0897: if (!(other instanceof ZipFileBase))
0898: return false;
0899: ZipFileBase o = (ZipFileBase) other;
0900: return getArchiveURI().equals(o.getArchiveURI())
0901: && resName.equals(o.resName);
0902: }
0903:
0904: protected abstract URI getArchiveURI();
0905:
0906: protected abstract long getSize() throws IOException;
0907:
0908: }
0909:
0910: private static class ZipFileObject extends ZipFileBase {
0911:
0912: /** The zipfile containing the entry.
0913: */
0914: protected final File archiveFile;
0915:
0916: ZipFileObject(final File archiveFile, final String folderName,
0917: final String baseName, long mtime) {
0918: super (folderName, baseName, mtime);
0919: assert archiveFile != null : "archiveFile == null"; //NOI18N
0920: this .archiveFile = archiveFile;
0921:
0922: }
0923:
0924: public InputStream openInputStream() throws IOException {
0925: class ZipInputStream extends InputStream {
0926:
0927: private ZipFile zipfile;
0928: private InputStream delegate;
0929:
0930: /**
0931: * Creates new ZipInputStream.
0932: * When ZipInputStream is created it owns the given ZipFile
0933: * and closes it when this InputStream is closed or IOException
0934: * is thrown by the constructer.
0935: */
0936: public ZipInputStream(ZipFile zf) throws IOException {
0937: assert zf != null;
0938: this .zipfile = zf;
0939: try {
0940: this .delegate = zf.getInputStream(new ZipEntry(
0941: resName));
0942: if (this .delegate == null) {
0943: throw new IOException();
0944: }
0945: } catch (IOException e) {
0946: try {
0947: this .zipfile.close();
0948: } catch (IOException e2) {/*Outher exception is more important*/
0949: }
0950: throw e;
0951: }
0952: }
0953:
0954: public int read() throws IOException {
0955: throw new java.lang.UnsupportedOperationException(
0956: "Not supported yet.");
0957: }
0958:
0959: public int read(byte b[], int off, int len)
0960: throws IOException {
0961: return delegate.read(b, off, len);
0962: }
0963:
0964: public int available() throws IOException {
0965: return this .delegate.available();
0966: }
0967:
0968: public void close() throws IOException {
0969: try {
0970: this .delegate.close();
0971: } finally {
0972: this .zipfile.close();
0973: }
0974: }
0975:
0976: }
0977: ;
0978: // long time = System.currentTimeMillis();
0979: ZipFile zf = new ZipFile(archiveFile);
0980: // System.out.println("ZF OPEN " + archiveFile.getPath() + " took: " + (System.currentTimeMillis() - time )+ "ms." );
0981: return new BufferedInputStream(new ZipInputStream(zf));
0982: }
0983:
0984: public URI getArchiveURI() {
0985: return this .archiveFile.toURI();
0986: }
0987:
0988: protected long getSize() throws IOException {
0989: ZipFile zf = new ZipFile(archiveFile);
0990: try {
0991: ZipEntry ze = zf.getEntry(this .resName);
0992: return ze == null ? 0L : ze.getSize();
0993: } finally {
0994: zf.close();
0995: }
0996: }
0997: }
0998:
0999: private static class FastZipFileObject extends ZipFileObject {
1000:
1001: private long offset;
1002:
1003: FastZipFileObject(final File archiveFile,
1004: final String folderName, final String baseName,
1005: long mtime, long offset) {
1006: super (archiveFile, folderName, baseName, mtime);
1007: this .offset = offset;
1008: }
1009:
1010: @Override
1011: public InputStream openInputStream() throws IOException {
1012: try {
1013: return new BufferedInputStream(FastJar.getInputStream(
1014: archiveFile, offset));
1015: } catch (IOException e) {
1016: return super .openInputStream();
1017: }
1018: }
1019:
1020: @Override
1021: public long getSize() throws IOException {
1022: try {
1023: ZipEntry e = FastJar.getZipEntry(archiveFile, offset);
1024: if (e != null) {
1025: long size = e.getSize();
1026: //When there is no size, csize and CRC in the LOC table
1027: //delegate to super, needs to take it from the CEN table
1028: //but we don't have the offset in CEN table here.
1029: if (size != -1) {
1030: return size;
1031: }
1032: }
1033: } catch (IOException e) {
1034: //Handled below
1035: }
1036: return super .getSize();
1037: }
1038: }
1039:
1040: private static class CachedZipFileObject extends ZipFileBase {
1041:
1042: private ZipFile zipFile;
1043:
1044: CachedZipFileObject(final ZipFile zipFile,
1045: final String folderName, final String baseName,
1046: long mtime) {
1047: super (folderName, baseName, mtime);
1048: assert zipFile != null : "archiveFile == null"; //NOI18N
1049: this .zipFile = zipFile;
1050: }
1051:
1052: public InputStream openInputStream() throws IOException {
1053: return new BufferedInputStream(this .zipFile
1054: .getInputStream(new ZipEntry(this .resName)));
1055: }
1056:
1057: public URI getArchiveURI() {
1058: return new File(this .zipFile.getName()).toURI();
1059: }
1060:
1061: protected long getSize() throws IOException {
1062: ZipEntry ze = this .zipFile.getEntry(this .resName);
1063: return ze == null ? 0L : ze.getSize();
1064: }
1065: }
1066:
1067: /** Temporay FileObject for parsing input stream.
1068: */
1069: private static class MemoryFileObject extends Base {
1070:
1071: private String fileName;
1072: private CharBuffer cb;
1073:
1074: public MemoryFileObject(String fileName, CharBuffer cb) {
1075: super ("", fileName); //NOI18N
1076: this .cb = cb;
1077: this .fileName = fileName;
1078: }
1079:
1080: /**
1081: * Get the character content of the file, if available.
1082: * @param ignoreEncodingErrors if true, encoding errros will be replaced by the
1083: * default translation character; otherwise they should be reported as diagnostics.
1084: * @throws UnsupportedOperationException if character access is not supported
1085: */
1086: public java.nio.CharBuffer getCharContent(
1087: boolean ignoreEncodingErrors)
1088: throws java.io.IOException {
1089: return cb;
1090: }
1091:
1092: public boolean delete() {
1093: // Do nothing
1094: return false;
1095: }
1096:
1097: public URI toUri() {
1098: return URI.create(this .nameWithoutExt);
1099: }
1100:
1101: public long getLastModified() {
1102: return System.currentTimeMillis(); // XXX
1103: }
1104:
1105: /**
1106: * Get an InputStream for this object.
1107: *
1108: * @return an InputStream for this object.
1109: * @throws UnsupportedOperationException if the byte access is not supported
1110: */
1111: public InputStream openInputStream() throws java.io.IOException {
1112: return new ByteArrayInputStream(cb.toString().getBytes(
1113: "UTF-8"));
1114: }
1115:
1116: /**
1117: * Get an OutputStream for this object.
1118: *
1119: * @return an OutputStream for this object.
1120: * @throws UnsupportedOperationException if byte access is not supported
1121: */
1122: public java.io.OutputStream openOutputStream()
1123: throws java.io.IOException {
1124: throw new UnsupportedOperationException();
1125: }
1126:
1127: /**
1128: * Get a reader for this object.
1129: *
1130: * @return a Reader for this file object.
1131: * @throws UnsupportedOperationException if character access is not supported
1132: * @throws IOException if an error occurs while opening the reader
1133: */
1134: public java.io.Reader openReader(boolean b)
1135: throws java.io.IOException {
1136: throw new UnsupportedOperationException();
1137: }
1138:
1139: /**
1140: * Get a writer for this object.
1141: * @throws UnsupportedOperationException if character access is not supported
1142: * @throws IOException if an error occurs while opening the writer
1143: */
1144: public java.io.Writer openWriter() throws java.io.IOException {
1145: throw new UnsupportedOperationException();
1146: }
1147:
1148: }
1149:
1150: private static class SimpleNameStringComparator implements
1151: Comparator<String> {
1152:
1153: public int compare(String o1, String o2) {
1154: return getSimpleName(o1).compareTo(getSimpleName(o2));
1155: }
1156:
1157: }
1158:
1159: private static class SimpleNameFileObjectComparator implements
1160: Comparator<JavaFileObject> {
1161:
1162: public int compare(JavaFileObject o1, JavaFileObject o2) {
1163:
1164: String n1 = getSimpleName(o1);
1165: String n2 = getSimpleName(o2);
1166:
1167: return n1.compareTo(n2);
1168: }
1169:
1170: }
1171:
1172: static final String encodingName = new OutputStreamWriter(
1173: new ByteArrayOutputStream()).getEncoding();
1174:
1175: }
|