blob: 028e1fa59154cf74b58da037dc3ac943c4540d35 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2013 Soyatec (http://www.soyatec.com), CEA, and others.
* 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
*
* Contributors:
* Soyatec - initial API and implementation
* Christian W. Damus (CEA) - Fix failure to propagate stream handlers of URLs (CDO)
*******************************************************************************/
package org.eclipse.xwt.internal.xml;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.eclipse.xwt.IConstants;
import org.eclipse.xwt.XWTException;
/**
* @author yyang
* @since 1.0
*/
public class DocumentRoot {
/**
* The file type(Can be resolved by java.io.File)
*/
public static final int TYPE_FILE = 0;
/**
* The path type(Can be resolved by java.net.URL)
*/
public static final int TYPE_PATH = 1;
/**
* The zip format.
*/
public static final int FORMAT_ZIP = 2;
/**
* The gzip format.
*/
public static final int FORMAT_GZIP = 3;
/**
* The plain format.
*/
public static final int FORMAT_NONE = 4;
private int type = TYPE_PATH;
private int format = FORMAT_NONE;
/**
* The absolute path.
*/
private String basePath;
/**
* The relative path.
*/
private URL baseURL;
/**
* The main file name.
*/
private String baseFile;
private static URL DOCUMENT_ROOT;
static {
try {
DOCUMENT_ROOT = new File(System.getProperty("user.dir")).toURI().toURL();
} catch (MalformedURLException e) {
DOCUMENT_ROOT = null;
}
}
public DocumentRoot() {
reset();
}
/*
* (non-Javadoc)
*
* @see org.soyatec.xaml.IDocumentRoot#getFormat()
*/
public int getFormat() {
return format;
}
/*
* (non-Javadoc)
*
* @see org.soyatec.xaml.IDocumentRoot#getType()
*/
public int getType() {
return type;
}
/*
* (non-Javadoc)
*
* @see org.soyatec.xaml.IDocumentRoot#getPath()
*/
public URL getPath() {
return baseURL;
}
/*
* (non-Javadoc)
*
* @see org.soyatec.xaml.IDocumentRoot#getFile()
*/
public String getFile() {
return baseFile;
}
/*
* (non-Javadoc)
*
* @see org.soyatec.xaml.IDocumentRoot#openStream()
*/
public InputStream openStream() throws IOException {
switch (format) {
case FORMAT_GZIP:
return new GZIPInputStream(new URL(basePath + "/" + baseFile).openStream());
default:
if ((basePath != null) && (basePath.length() > 0)) {
return new URL(baseURL, basePath + "/" + baseFile).openStream(); // preserve the stream handler
}
return new URL(baseURL, baseFile).openStream(); // preserve the stream handler
}
}
/*
* (non-Javadoc)
*
* @see org.soyatec.xaml.IDocumentRoot#openStream(java.lang.String)
*/
public InputStream openStream(String path) throws IOException {
assert path != null;
InputStream in = null;
if (path.indexOf(':') < 0) {
if (format == FORMAT_ZIP) {
// If it is zip format, that means all directories and files in
// zip file are extracted to temporary directory.
File file = new File(basePath + "/" + path);
in = new FileInputStream(file);
} else {
// Else, maybe the file parent or url path.
switch (type) {
case TYPE_FILE:
File file;
if (path.startsWith("/")) {
// Use absolute path
file = new File(path);
} else {
// Use relative path
file = new File(basePath + "/" + path);
}
in = new FileInputStream(file);
break;
case TYPE_PATH:
URL url = new URL(basePath + "/" + path);
in = url.openStream();
break;
}
}
} else {
if (path.startsWith("classpath:")) {
// Use class path
String p = path.substring(10);
in = getClass().getResourceAsStream(p);
if (in == null) {
in = String.class.getResourceAsStream(p);
}
if (in == null) {
in = Thread.currentThread().getClass().getResourceAsStream(p);
}
if (in == null) {
throw new IOException("File not found in classpath: " + p);
}
} else {
File file = new File(path);
if (file.exists()) {
// Use Windows File path. For example, G:\\somefile.txt
in = new FileInputStream(file);
} else {
// Use URL path
in = new URL(path).openStream();
}
}
}
// Automatically select the stream type
PushbackInputStream pis = new PushbackInputStream(in, 4);
if (Format.isGZIP(pis)) {
return new GZIPInputStream(pis);
} else if (Format.isZIP(pis)) {
ZipInputStream zis = new ZipInputStream(pis);
// Skip entry of directory or file which contains directory
// path("someDirectory/someFile.suffix").
for (ZipEntry entry = zis.getNextEntry(); entry != null && (entry.isDirectory() || entry.getName().indexOf('/') != -1); entry = zis.getNextEntry())
;
return zis;
} else {
return pis;
}
}
/**
* This method is used in <code>ModelLoader</code>
*
* @param file
* the xaml file path.
*/
protected void init(InputStream inputStream, URL url) throws IOException {
File file = null;
try {
file = "file".equals(url.getProtocol()) ? new File(url.toURI()) : null;
} catch (URISyntaxException e) {
// not a valid file URL. Fine
}
if((inputStream == null) && (file != null) && file.exists()) {
// Is file
init(file);
} else {
// Is URL
basePath = null;
baseURL = url;
PushbackInputStream pis = null;
boolean shouldClose_pis = false;
if (inputStream instanceof PushbackInputStream) {
pis = (PushbackInputStream) inputStream;
} else {
if (inputStream == null) {
pis = new PushbackInputStream(baseURL.openStream(), 4);
shouldClose_pis = true;
} else {
pis = new PushbackInputStream(inputStream, 4);
}
}
if (Format.isGZIP(pis)) {
format = FORMAT_GZIP;
} else if (Format.isZIP(pis)) {
format = FORMAT_ZIP;
File tempDir = extractZipToTemporary(pis);
basePath = "file:/" + tempDir.getAbsolutePath();
baseFile = getMainFile(tempDir);
}
if (shouldClose_pis) {
pis.close();
}
if (basePath == null) {
String path = url.getPath();
while (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
int lastIndex = path.lastIndexOf("/");
if (lastIndex > 0) {
basePath = path.substring(0, lastIndex);
baseFile = path.substring(lastIndex + 1);
baseURL = new URL(url, basePath); // be sure to preserve the stream handler
} else if ("file".equals(url.getProtocol())) {
basePath = System.getProperty("user.dir");
baseURL = new File(basePath).toURI().toURL();
baseFile = path;
} else {
basePath = "";
baseFile = path;
baseURL = new URL(url, basePath); // be sure to preserve the stream handler
}
}
type = TYPE_PATH;
}
}
/**
* This method is used in <code>ModelLoader</code>
*/
protected void reset() {
type = TYPE_PATH;
format = FORMAT_NONE;
baseURL = DOCUMENT_ROOT;
basePath = DOCUMENT_ROOT.toString();
baseFile = null;
}
/**
* This method is used in <code>ModelLoader</code>
*
* @param file
* the XAML file.
*/
private void init(File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
PushbackInputStream pis = new PushbackInputStream(fis, 4);
baseURL = file.getParentFile().toURI().toURL();
basePath = baseURL.toString();
baseFile = file.getName();
if (Format.isGZIP(pis)) {
format = FORMAT_GZIP;
} else if (Format.isZIP(pis)) {
format = FORMAT_ZIP;
File tempDir = extractZipToTemporary(pis);
basePath = "file:/" + tempDir.getAbsolutePath();
baseFile = getMainFile(tempDir);
baseURL = file.toURI().toURL();
}
type = TYPE_FILE;
}
private String getMainFile(File tempDir) {
File[] tempFiles = tempDir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isFile() && pathname.getName().toLowerCase().endsWith(IConstants.XWT_EXTENSION_SUFFIX);
}
});
for (File tempFile : tempFiles) {
return tempFile.getName();
}
return null;
}
/**
* Extract zip stream to temporary directory.
*
* @param stream
* zip stream.
* @return Returns the directory file the zip stream extracted.
*/
private File extractZipToTemporary(InputStream stream) throws IOException {
File file = new File(System.getProperty("java.io.tmpdir") + "/cb" + System.currentTimeMillis() + Math.random());
if (!file.mkdir()) {
throw new XWTException("Folder creation fails: " + file.toString());
}
file.deleteOnExit();
String directory = file.getAbsolutePath();
ZipInputStream in = new ZipInputStream(stream);
ZipEntry z;
while ((z = in.getNextEntry()) != null) {
if (z.isDirectory()) {
String name = z.getName();
name = name.substring(0, name.length() - 1);
File f = new File(directory + File.separator + name);
if (!f.mkdir()) {
throw new XWTException("Folder creation fails: " + f.toString());
}
f.deleteOnExit();
} else {
File f = new File(directory + File.separator + z.getName());
if (!f.createNewFile()) {
throw new XWTException("File creation fails: " + f.toString());
}
f.deleteOnExit();
FileOutputStream out = new FileOutputStream(f);
byte[] cache = new byte[4096];
for (int i = in.read(cache); i != -1; i = in.read(cache)) {
out.write(cache, 0, i);
}
out.close();
}
}
in.close();
return file;
}
/**
* File format uitility.
* <p>
* To check the format of file or stream.
* </p>
*
*/
static class Format {
/**
* Check the stream is a gzip format or not.
*
* @param stream
* the checked stream.
* @return Returns true is the stream is a gzip format.
*/
public static boolean isGZIP(PushbackInputStream stream) throws IOException {
assert stream != null;
byte[] cachedBytes = new byte[2];
if (stream.read(cachedBytes) != cachedBytes.length) {
throw new RuntimeException("data content wrong.");
}
stream.unread(cachedBytes);
// GZIP's header data starts with two bytes{1F,8B},
if ((cachedBytes[0] & 0xff) == 0x1f && (cachedBytes[1] & 0xff) == 0x8b) {
return true;
} else {
return false;
}
}
/**
* Check the file is a gzip format or not.
*
* @param file
* the checked file.
* @return Returns true is the file is a gzip format.
*/
public static boolean isGZIP(File file) throws IOException {
assert file != null;
FileInputStream fis = new FileInputStream(file);
PushbackInputStream pis = new PushbackInputStream(fis, 2);
boolean returnValue = isGZIP(pis);
pis.close();
return returnValue;
}
/**
* Check the stream is a zip format or not.
*
* @param stream
* the checked stream.
* @return Returns true is the stream is a zip format.
*/
public static boolean isZIP(PushbackInputStream stream) throws IOException {
assert stream != null;
byte[] cachedBytes = new byte[4];
if (stream.read(cachedBytes) != cachedBytes.length) {
throw new RuntimeException("data content wrong.");
}
stream.unread(cachedBytes);
// ZIP's header data starts with four bytes{0x50, 0x4b, 0x03, 0x04},
if ((cachedBytes[0] & 0xff) == 0x50 && (cachedBytes[1] & 0xff) == 0x4b && (cachedBytes[2] & 0xff) == 0x03 && (cachedBytes[3] & 0xff) == 0x04) {
return true;
} else {
return false;
}
}
/**
* Check the file is a zip format or not.
*
* @param file
* the checked file.
* @return Returns true is the file is a zip format.
*/
public static boolean isZIP(File file) throws IOException {
assert file != null;
FileInputStream fis = new FileInputStream(file);
PushbackInputStream pis = new PushbackInputStream(fis, 4);
boolean returnValue = isZIP(pis);
pis.close();
return returnValue;
}
}
}