From commits-return-2521-apmail-jackrabbit-commits-archive=jackrabbit.apache.org@jackrabbit.apache.org Wed Jul 12 13:34:31 2006 Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 15554 invoked from network); 12 Jul 2006 13:34:30 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 12 Jul 2006 13:34:30 -0000 Received: (qmail 25391 invoked by uid 500); 12 Jul 2006 13:34:27 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 25350 invoked by uid 500); 12 Jul 2006 13:34:27 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 25225 invoked by uid 99); 12 Jul 2006 13:34:27 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Jul 2006 06:34:27 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 12 Jul 2006 06:34:19 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id B0A201A981D; Wed, 12 Jul 2006 06:33:59 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r421270 [2/23] - in /jackrabbit/trunk/contrib/spi: ./ commons/ commons/src/ commons/src/main/ commons/src/main/java/ commons/src/main/java/org/ commons/src/main/java/org/apache/ commons/src/main/java/org/apache/jackrabbit/ commons/src/main/... Date: Wed, 12 Jul 2006 13:33:27 -0000 To: commits@jackrabbit.apache.org From: angela@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20060712133359.B0A201A981D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/FileSystemResource.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/FileSystemResource.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/FileSystemResource.java (added) +++ jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/FileSystemResource.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.jackrabbit.fs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A FileSystemResource represents a resource (i.e. file) in a + * FileSystem. + */ +public class FileSystemResource { + + protected final FileSystem fs; + + protected final String path; + + static { + // preload FileSystemPathUtil to prevent classloader issues during shutdown + FileSystemPathUtil.class.hashCode(); + } + + /** + * Creates a new FileSystemResource + * + * @param fs the FileSystem where the resource is located + * @param path the path of the resource in the FileSystem + */ + public FileSystemResource(FileSystem fs, String path) { + if (fs == null) { + throw new IllegalArgumentException("invalid file system argument"); + } + this.fs = fs; + + if (path == null) { + throw new IllegalArgumentException("invalid path argument"); + } + this.path = path; + } + + /** + * Returns the FileSystem where this resource is located. + * + * @return the FileSystem where this resource is located. + */ + public FileSystem getFileSystem() { + return fs; + } + + /** + * Returns the path of this resource. + * + * @return the path of this resource. + */ + public String getPath() { + return path; + } + + /** + * Returns the parent directory of this resource. + * + * @return the parent directory. + */ + public String getParentDir() { + return FileSystemPathUtil.getParentDir(path); + } + + /** + * Returns the name of this resource. + * + * @return the name. + */ + public String getName() { + return FileSystemPathUtil.getName(path); + } + + /** + * Creates the parent directory of this resource, including any necessary + * but nonexistent parent directories. + * + * @throws FileSystemException + */ + public synchronized void makeParentDirs() throws FileSystemException { + String parentDir = getParentDir(); + if (!fs.exists(parentDir)) { + fs.createFolder(parentDir); + } + } + + /** + * Deletes this resource. + * Same as {@link #delete(false)}. + * + * @see FileSystem#deleteFile + */ + public void delete() throws FileSystemException { + delete(false); + } + + /** + * Deletes this resource. + * + * @param pruneEmptyParentDirs if true, empty parent folders will + * automatically be deleted + * @see FileSystem#deleteFile + */ + public synchronized void delete(boolean pruneEmptyParentDirs) throws FileSystemException { + fs.deleteFile(path); + if (pruneEmptyParentDirs) { + // prune empty parent folders + String parentDir = FileSystemPathUtil.getParentDir(path); + while (!parentDir.equals(FileSystem.SEPARATOR) + && fs.exists(parentDir) + && !fs.hasChildren(parentDir)) { + fs.deleteFolder(parentDir); + parentDir = FileSystemPathUtil.getParentDir(parentDir); + } + } + } + + /** + * @see FileSystem#exists + */ + public boolean exists() throws FileSystemException { + return fs.exists(path); + } + + /** + * @see FileSystem#getInputStream + */ + public InputStream getInputStream() throws FileSystemException { + return fs.getInputStream(path); + } + + /** + * Spools this resource to the given output stream. + * + * @param out output stream where to spool the resource + * @throws FileSystemException if the input stream for this resource could + * not be obtained + * @throws IOException if an error occurs while while spooling + * @see FileSystem#getInputStream + */ + public void spool(OutputStream out) throws FileSystemException, IOException { + InputStream in = fs.getInputStream(path); + try { + byte[] buffer = new byte[8192]; + int read; + while ((read = in.read(buffer)) > 0) { + out.write(buffer, 0, read); + } + } finally { + try { + in.close(); + } catch (IOException ioe) { + } + } + } + + /** + * @see FileSystem#getOutputStream + */ + public OutputStream getOutputStream() throws FileSystemException { + return fs.getOutputStream(path); + } + + /** + * @see FileSystem#getRandomAccessOutputStream + */ + public RandomAccessOutputStream getRandomAccessOutputStream() + throws FileSystemException { + return fs.getRandomAccessOutputStream(path); + } + + /** + * @see FileSystem#lastModified + */ + public long lastModified() throws FileSystemException { + return fs.lastModified(path); + } + + /** + * @see FileSystem#length + */ + public long length() throws FileSystemException { + return fs.length(path); + } + + /** + * @see FileSystem#touch + */ + public void touch() throws FileSystemException { + fs.touch(path); + } + + /** + * @see FileSystem#move + */ + public void move(String destPath) throws FileSystemException { + fs.move(path, destPath); + } + + //-------------------------------------------< java.lang.Object overrides > + /** + * Returns the path string of this resource. This is just the + * string returned by the {@link #getPath} method. + * + * @return The path string of this resource + */ + public String toString() { + return getPath(); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof FileSystemResource) { + FileSystemResource other = (FileSystemResource) obj; + return (path == null ? other.path == null : path.equals(other.path)) + && (fs == null ? other.fs == null : fs.equals(other.fs)); + } + return false; + } + + /** + * Returns zero to satisfy the Object equals/hashCode contract. + * This class is mutable and not meant to be used as a hash key. + * + * @return always zero + * @see Object#hashCode() + */ + public int hashCode() { + return 0; + } + +} Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/FileSystemResource.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/FileSystemResource.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/RandomAccessOutputStream.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/RandomAccessOutputStream.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/RandomAccessOutputStream.java (added) +++ jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/RandomAccessOutputStream.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.jackrabbit.fs; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Extends the regular java.io.OutputStream with a random + * access facility. Multiple write() operations can be + * positioned off sequence with the {@link #seek} method. + */ +public abstract class RandomAccessOutputStream extends OutputStream { + + /** + * Sets the current position in the resource where the next write + * will occur. + * + * @param position the new position in the resource. + * @throws IOException if an error occurs while seeking to the position. + */ + public abstract void seek(long position) throws IOException; +} Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/RandomAccessOutputStream.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/fs/RandomAccessOutputStream.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/QValue.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/QValue.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/QValue.java (added) +++ jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/QValue.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,932 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.jackrabbit.value; + +import org.apache.jackrabbit.name.QName; +import org.apache.jackrabbit.name.Path; +import org.apache.jackrabbit.uuid.UUID; +import org.apache.jackrabbit.util.ISO8601; +import org.apache.jackrabbit.util.TransientFileFactory; +import org.apache.jackrabbit.fs.FileSystemResource; +import org.apache.jackrabbit.fs.FileSystemException; + +import javax.jcr.RepositoryException; +import javax.jcr.PropertyType; +import javax.jcr.Property; +import javax.jcr.ValueFormatException; +import java.io.InputStream; +import java.io.IOException; +import java.io.File; +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.Calendar; +import java.util.Arrays; + +/** +* QValue represents the qualified format of a property value. + *

+ * The following table specifies the qualified format for every property type: + *

+ * 
+ * PropertyType
+ * STRING
+ * LONG
+ * DOUBLE
+ * DATE
+ * BOOLEAN
+ * NAME
+ * PATH
+ * BINARY
+ * REFERENCE
+ * 
Internal Format
String
Long
Double
Calendar
Boolean
QName
Path
BLOBFileValue
String
+ *
+ */ +public class QValue { + + public static final QValue[] EMPTY_ARRAY = new QValue[0]; + + public static final QValue BOOLEAN_TRUE = create(true); + public static final QValue BOOLEAN_FALSE = create(false); + + /** + * the default encoding + */ + private static final String DEFAULT_ENCODING = "UTF-8"; + + private final Object val; + private final int type; + + //----------------------------------------------------< Factory methods >--- + /** + * @param value + * @return + */ + public static QValue create(String value) { + return new QValue(value); + } + + /** + * @param qValue + * @return + */ + public static QValue create(String qValue, int type) { + if (qValue == null) { + throw new IllegalArgumentException("Cannot create QValue from null value."); + } + switch (type) { + case PropertyType.BOOLEAN: + return new QValue(Boolean.valueOf(qValue).booleanValue()); + case PropertyType.DATE: + return new QValue(ISO8601.parse(qValue)); + case PropertyType.DOUBLE: + return new QValue(Double.valueOf(qValue).doubleValue()); + case PropertyType.LONG: + return new QValue(Long.valueOf(qValue).longValue()); + case PropertyType.REFERENCE: + // DIFF JR: references are not forced to represent a UUID object + return new QValue(qValue, true); + case PropertyType.PATH: + return new QValue(Path.valueOf(qValue)); + case PropertyType.NAME: + return new QValue(QName.valueOf(qValue)); + case PropertyType.STRING: + return new QValue(qValue); + case PropertyType.BINARY: + throw new IllegalArgumentException("this method does not support the type PropertyType.BINARY"); + default: + throw new IllegalArgumentException("illegal type"); + } + } + + /** + * @param value + * @return + */ + public static QValue create(long value) { + return new QValue(value); + } + + /** + * @param value + * @return + */ + public static QValue create(double value) { + return new QValue(value); + } + + /** + * @param value + * @return + */ + public static QValue create(Calendar value) { + return new QValue(value); + } + + /** + * @param value + * @return + */ + public static QValue create(boolean value) { + return new QValue(value); + } + + /** + * @param value + * @return + */ + public static QValue create(byte[] value) { + return new QValue(new BLOBFileValue(value)); + } + + /** + * @param value + * @return + * @throws IOException + */ + public static QValue create(InputStream value) throws IOException { + return new QValue(new BLOBFileValue(value)); + } + + /** + * @param value + * @param type + * @return + * @throws IOException + */ + public static QValue create(InputStream value, int type) throws IOException, RepositoryException { + if (value == null) { + throw new IllegalArgumentException("Cannot create QValue from null value."); + } + switch (type) { + case PropertyType.BINARY: + return new QValue(new BLOBFileValue(value)); + + case PropertyType.BOOLEAN: + case PropertyType.DATE: + case PropertyType.DOUBLE: + case PropertyType.LONG: + case PropertyType.REFERENCE: + case PropertyType.PATH: + case PropertyType.NAME: + case PropertyType.STRING: + // convert stream value to String + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int read; + while ((read = value.read(buffer)) > 0) { + out.write(buffer, 0, read); + } + byte[] data = out.toByteArray(); + String text = new String(data, DEFAULT_ENCODING); + return create(new String(data, DEFAULT_ENCODING), type); + + } catch (UnsupportedEncodingException e) { + throw new RepositoryException(DEFAULT_ENCODING + " not supported on this platform", e); + } catch (IOException e) { + throw new RepositoryException("conversion from stream to string failed", e); + } finally { + value.close(); + } + default: + throw new IllegalArgumentException("illegal type"); + } + } + + /** + * @param value + * @param temp + * @return + * @throws IOException + */ + public static QValue create(InputStream value, boolean temp) throws IOException { + return new QValue(new BLOBFileValue(value, temp)); + } + + /** + * @param value + * @return + * @throws IOException + */ + public static QValue create(File value) throws IOException { + return new QValue(new BLOBFileValue(value)); + } + + /** + * @param value + * @return + */ + public static QValue create(QName value) { + return new QValue(value); + } + + /** + * @param values + * @return + */ + public static QValue[] create(QName[] values) { + QValue[] ret = new QValue[values.length]; + for (int i = 0; i < values.length; i++) { + ret[i] = new QValue(values[i]); + } + return ret; + } + + /** + * @param values + * @return + */ + public static QValue[] create(String[] values) { + QValue[] ret = new QValue[values.length]; + for (int i = 0; i < values.length; i++) { + ret[i] = new QValue(values[i]); + } + return ret; + } + + /** + * @param values + * @return + */ + public static QValue[] create(Calendar[] values) { + QValue[] ret = new QValue[values.length]; + for (int i = 0; i < values.length; i++) { + ret[i] = new QValue(values[i]); + } + return ret; + } + + /** + * @param value + * @return + */ + public static QValue create(Path value) { + return new QValue(value); + } + + /** + * @param value + * @return + */ + public static QValue create(UUID value) { + return new QValue(value); + } + + /** + * @return + */ + public int getType() { + return type; + } + + /** + * + * @return + */ + public String getString() throws RepositoryException { + if (type == PropertyType.DATE) { + return ISO8601.format((Calendar) val); + } else if (type == PropertyType.BINARY) { + return ((BLOBFileValue) val).getString(); + } else { + return val.toString(); + } + } + + /** + * + * @return + * @throws RepositoryException + */ + public InputStream getStream() throws RepositoryException { + if (type == PropertyType.BINARY) { + return ((BLOBFileValue) val).getStream(); + } else { + try { + // convert via string + return new ByteArrayInputStream(getString().getBytes(DEFAULT_ENCODING)); + } catch (UnsupportedEncodingException e) { + throw new RepositoryException(DEFAULT_ENCODING + " is not supported encoding on this platform", e); + } + } + } + + /** + * Frees temporarily allocated resources such as temporary file, buffer, etc. + */ + public void discard() { + if (type == PropertyType.BINARY) { + ((BLOBFileValue) val).discard(); + } + // else: nothing to do. + } + + /** + * Returns the length of the internal value.
+ * NOTE: for {@link PropertyType#NAME} and {@link PropertyType#PATH} the + * length of the internal value must not be used for indicating the length + * of a property such as retrieved by calling {@link Property#getLength()} + * and {@link Property#getLengths()}. + * + * @return length of the internal value. + */ + public long getLength() throws RepositoryException { + if (type == PropertyType.BINARY) { + return ((BLOBFileValue) val).getLength(); + } else { + return getString().length(); + } + } + + /** + * @return + * @throws RepositoryException + */ + public QValue createCopy() throws RepositoryException { + switch (type) { + case PropertyType.BINARY: + try { + InputStream stream = ((BLOBFileValue) val).getStream(); + try { + return new QValue(new BLOBFileValue(stream)); + } finally { + try { + stream.close(); + } catch (IOException e) { + // ignore + } + } + } catch (IOException ioe) { + throw new RepositoryException("Failed to copy binary value", ioe); + } + case PropertyType.BOOLEAN: + return new QValue(((Boolean) val).booleanValue()); + case PropertyType.DATE: + return new QValue((Calendar) val); + case PropertyType.DOUBLE: + return new QValue(((Double) val).doubleValue()); + case PropertyType.LONG: + return new QValue(((Long) val).longValue()); + case PropertyType.REFERENCE: + return new QValue((String) val, true); + case PropertyType.PATH: + return new QValue((Path) val); + case PropertyType.NAME: + return new QValue((QName) val); + case PropertyType.STRING: + return new QValue((String) val); + default: + throw new RepositoryException("Illegal internal value type"); + } + } + + //-------------------------------------------------------------< Object >--- + /** + * Returns the string representation of this internal value. If this is a + * binary value then the path of its backing file will be returned. + * + * @return string representation of this internal value + */ + public String toString() { + if (type == PropertyType.DATE) { + return ISO8601.format((Calendar) val); + } else { + return val.toString(); + } + } + + /** + * + * @param obj + * @return + * @see Object#equals(Object) + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof QValue) { + QValue other = (QValue) obj; + return val.equals(other.val); + } + return false; + } + + /** + * @return the hashCode of the internal value object. + * @see Object#hashCode() + */ + public int hashCode() { + return val.hashCode(); + } + + //-------------------------------------------------------< implementation > + private QValue(String value) { + val = value; + type = PropertyType.STRING; + } + + // DIFF JR: constructor added + private QValue(String value, boolean isReference) { + val = value; + type = (isReference) ? PropertyType.REFERENCE : PropertyType.STRING; + } + + private QValue(QName value) { + val = value; + type = PropertyType.NAME; + } + + private QValue(long value) { + val = new Long(value); + type = PropertyType.LONG; + } + + private QValue(double value) { + val = new Double(value); + type = PropertyType.DOUBLE; + } + + private QValue(Calendar value) { + val = value; + type = PropertyType.DATE; + } + + private QValue(boolean value) { + val = new Boolean(value); + type = PropertyType.BOOLEAN; + } + + private QValue(BLOBFileValue value) { + val = value; + type = PropertyType.BINARY; + } + + private QValue(Path value) { + val = value; + type = PropertyType.PATH; + } + + private QValue(UUID value) { + // DIFF JR: reference value must not represent a UUID object + val = (value == null) ? null : value.toString(); + type = PropertyType.REFERENCE; + } + + //--------------------------------------------------------< Inner Class >--- + /** + * BLOBFileValue represents a binary Value which is + * backed by a resource or byte[]. Unlike BinaryValue it has no + * state, i.e. the getStream() method always returns a fresh + * InputStream instance. + */ + static class BLOBFileValue { + /** + * empty array + */ + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + + /** + * max size for keeping tmp data in memory + */ + private static final int MAX_BUFFER_SIZE = 0x10000; + + /** + * underlying file + */ + private final File file; + + /** + * flag indicating if this instance represents a temporary value + * whose dynamically allocated resources can be explicitly freed on + * {@link #discard()}. + */ + private final boolean temp; + + /** + * buffer for small-sized data + */ + private byte[] buffer = EMPTY_BYTE_ARRAY; + + /** + * underlying file system resource + */ + private final FileSystemResource fsResource; + + /** + * converted text + */ + private String text = null; + + /** + * Creates a new BLOBFileValue instance from an + * InputStream. The contents of the stream is spooled + * to a temporary file or to a byte buffer if its size is smaller than + * {@link #MAX_BUFFER_SIZE}. + *

+ * The new instance represents a temporary value whose dynamically + * allocated resources will be freed explicitly on {@link #discard()}. + * + * @param in stream to be represented as a BLOBFileValue instance + * @throws IOException if an error occurs while reading from the stream or + * writing to the temporary file + */ + BLOBFileValue(InputStream in) throws IOException { + this(in, true); + } + + /** + * Creates a new BLOBFileValue instance from an + * InputStream. The contents of the stream is spooled + * to a temporary file or to a byte buffer if its size is smaller than + * {@link #MAX_BUFFER_SIZE}. + *

+ * The temp parameter governs whether dynamically allocated + * resources will be freed explicitly on {@link #discard()}. Note that any + * dynamically allocated resources (temp file/buffer) will be freed + * implicitly once this instance has been gc'ed. + * + * @param in stream to be represented as a BLOBFileValue instance + * @param temp flag indicating whether this instance represents a + * temporary value whose resources can be explicitly freed + * on {@link #discard()}. + * @throws IOException if an error occurs while reading from the stream or + * writing to the temporary file + */ + BLOBFileValue(InputStream in, boolean temp) throws IOException { + byte[] spoolBuffer = new byte[0x2000]; + int read; + int len = 0; + OutputStream out = null; + File spoolFile = null; + try { + while ((read = in.read(spoolBuffer)) > 0) { + if (out != null) { + // spool to temp file + out.write(spoolBuffer, 0, read); + len += read; + } else if (len + read > MAX_BUFFER_SIZE) { + // threshold for keeping data in memory exceeded; + // create temp file and spool buffer contents + TransientFileFactory fileFactory = TransientFileFactory.getInstance(); + spoolFile = fileFactory.createTransientFile("bin", null, null); + out = new FileOutputStream(spoolFile); + out.write(buffer, 0, len); + out.write(spoolBuffer, 0, read); + buffer = null; + len += read; + } else { + // reallocate new buffer and spool old buffer contents + byte[] newBuffer = new byte[len + read]; + System.arraycopy(buffer, 0, newBuffer, 0, len); + System.arraycopy(spoolBuffer, 0, newBuffer, len, read); + buffer = newBuffer; + len += read; + } + } + } finally { + if (out != null) { + out.close(); + } + } + + // init vars + file = spoolFile; + fsResource = null; + this.temp = temp; + } + + /** + * Creates a new BLOBFileValue instance from a + * byte[] array. + * + * @param bytes byte array to be represented as a BLOBFileValue + * instance + */ + BLOBFileValue(byte[] bytes) { + buffer = bytes; + file = null; + fsResource = null; + // this instance is not backed by a temporarily allocated buffer + temp = false; + } + + /** + * Creates a new BLOBFileValue instance from a File. + * + * @param file file to be represented as a BLOBFileValue instance + * @throws IOException if the file can not be read + */ + BLOBFileValue(File file) throws IOException { + String path = file.getCanonicalPath(); + if (!file.isFile()) { + throw new IOException(path + ": the specified file does not exist"); + } + if (!file.canRead()) { + throw new IOException(path + ": the specified file can not be read"); + } + this.file = file; + // this instance is backed by a 'real' file; set virtual fs resource to null + fsResource = null; + // this instance is not backed by temporarily allocated resource/buffer + temp = false; + } + + /** + * Creates a new BLOBFileValue instance from a resource in the + * virtual file system. + * + * @param fsResource resource in virtual file system + * @throws IOException if the resource can not be read + */ + BLOBFileValue(FileSystemResource fsResource) throws IOException { + try { + if (!fsResource.exists()) { + throw new IOException(fsResource.getPath() + + ": the specified resource does not exist"); + } + } catch (FileSystemException fse) { + throw new IOException(fsResource.getPath() + + ": Error while creating value: " + fse.toString()); + } + // this instance is backed by a resource in the virtual file system + this.fsResource = fsResource; + // set 'real' file to null + file = null; + // this instance is not backed by temporarily allocated resource/buffer + temp = false; + } + + /** + * Returns the length of this BLOBFileValue. + * + * @return The length, in bytes, of this BLOBFileValue, + * or -1L if the length can't be determined. + */ + long getLength() { + if (file != null) { + // this instance is backed by a 'real' file + if (file.exists()) { + return file.length(); + } else { + return -1; + } + } else if (fsResource != null) { + // this instance is backed by a resource in the virtual file system + try { + return fsResource.length(); + } catch (FileSystemException fse) { + return -1; + } + } else { + // this instance is backed by an in-memory buffer + return buffer.length; + } + } + + /** + * Frees temporarily allocated resources such as temporary file, buffer, etc. + * If this BLOBFileValue is backed by a persistent resource + * calling this method will have no effect. + * + * @see #delete() + * @see #delete(boolean) + */ + void discard() { + if (!temp) { + // do nothing if this instance is not backed by temporarily + // allocated resource/buffer + return; + } + if (file != null) { + // this instance is backed by a temp file + file.delete(); + } else if (buffer != null) { + // this instance is backed by an in-memory buffer + buffer = EMPTY_BYTE_ARRAY; + } + } + + /** + * Deletes the persistent resource backing this BLOBFileValue. + * Same as {@link #delete(false)}. + *

+ * If this BLOBFileValue is not backed by a persistent + * resource calling this method will have no effect. + * + * @see #discard() + */ + void delete() { + if (!temp) { + delete(false); + } + } + + /** + * Deletes the persistent resource backing this BLOBFileValue. + * + * @param pruneEmptyParentDirs if true, empty parent directories + * will automatically be deleted + */ + void delete(boolean pruneEmptyParentDirs) { + if (file != null) { + // this instance is backed by a 'real' file + file.delete(); + if (pruneEmptyParentDirs) { + // prune empty parent directories + File parent = file.getParentFile(); + while (parent != null && parent.delete()) { + parent = parent.getParentFile(); + } + } + } else if (fsResource != null) { + // this instance is backed by a resource in the virtual file system + try { + fsResource.delete(pruneEmptyParentDirs); + } catch (FileSystemException fse) { + // ignore + } + } else { + // this instance is backed by an in-memory buffer + buffer = EMPTY_BYTE_ARRAY; + } + } + + /** + * + * @return + * @throws ValueFormatException + * @throws IllegalStateException + * @throws RepositoryException + */ + String getString() + throws ValueFormatException, IllegalStateException, + RepositoryException { + if (text == null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + spool(out); + byte[] data = out.toByteArray(); + text = new String(data, DEFAULT_ENCODING); + } catch (UnsupportedEncodingException e) { + throw new RepositoryException(DEFAULT_ENCODING + + " not supported on this platform", e); + } catch (IOException e) { + throw new ValueFormatException("conversion from stream to string failed", e); + } finally { + try { + out.close(); + } catch (IOException e) { + // ignore + } + } + } + return text; + } + + /** + * + * @return + * @throws IllegalStateException + * @throws RepositoryException + */ + InputStream getStream() + throws IllegalStateException, RepositoryException { + // always return a 'fresh' stream + if (file != null) { + // this instance is backed by a 'real' file + try { + return new FileInputStream(file); + } catch (FileNotFoundException fnfe) { + throw new RepositoryException("file backing binary value not found", + fnfe); + } + } else if (fsResource != null) { + // this instance is backed by a resource in the virtual file system + try { + return fsResource.getInputStream(); + } catch (FileSystemException fse) { + throw new RepositoryException(fsResource.getPath() + + ": the specified resource does not exist", fse); + } + } else { + return new ByteArrayInputStream(buffer); + } + } + + /** + * Spools the contents of this BLOBFileValue to the given + * output stream. + * + * @param out output stream + * @throws RepositoryException if the input stream for this + * BLOBFileValue could not be obtained + * @throws IOException if an error occurs while while spooling + */ + private void spool(OutputStream out) throws RepositoryException, IOException { + InputStream in; + if (file != null) { + // this instance is backed by a 'real' file + try { + in = new FileInputStream(file); + } catch (FileNotFoundException fnfe) { + throw new RepositoryException("file backing binary value not found", + fnfe); + } + } else if (fsResource != null) { + // this instance is backed by a resource in the virtual file system + try { + in = fsResource.getInputStream(); + } catch (FileSystemException fse) { + throw new RepositoryException(fsResource.getPath() + + ": the specified resource does not exist", fse); + } + } else { + // this instance is backed by an in-memory buffer + in = new ByteArrayInputStream(buffer); + } + try { + byte[] buffer = new byte[0x2000]; + int read; + while ((read = in.read(buffer)) > 0) { + out.write(buffer, 0, read); + } + } finally { + try { + in.close(); + } catch (IOException ignore) { + } + } + } + + //-------------------------------------------< java.lang.Object overrides > + /** + * Returns a string representation of this BLOBFileValue + * instance. The string representation of a resource backed value is + * the path of the underlying resource. If this instance is backed by an + * in-memory buffer the generic object string representation of the byte + * array will be used instead. + * + * @return A string representation of this BLOBFileValue instance. + */ + public String toString() { + if (file != null) { + // this instance is backed by a 'real' file + return file.toString(); + } else if (fsResource != null) { + // this instance is backed by a resource in the virtual file system + return fsResource.toString(); + } else { + // this instance is backed by an in-memory buffer + return buffer.toString(); + } + } + + /** + * {@inheritDoc} + */ + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BLOBFileValue) { + BLOBFileValue other = (BLOBFileValue) obj; + return ((file == null ? other.file == null : file.equals(other.file)) + && (fsResource == null ? other.fsResource == null : fsResource.equals(other.fsResource)) + && Arrays.equals(buffer, other.buffer)); + } + return false; + } + + // TODO: obj. is not mutable. -> provide impl of hashCode. + /** + * Returns zero to satisfy the Object equals/hashCode contract. + * This class is mutable and not meant to be used as a hash key. + * + * @return always zero + * @see Object#hashCode() + */ + public int hashCode() { + return 0; + } + } +} Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/QValue.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/QValue.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/ValueFormat.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/ValueFormat.java?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/ValueFormat.java (added) +++ jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/ValueFormat.java Wed Jul 12 06:33:19 2006 @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.jackrabbit.value; + +import org.apache.jackrabbit.name.NamespaceResolver; +import org.apache.jackrabbit.name.NameException; +import org.apache.jackrabbit.name.NoPrefixDeclaredException; +import org.apache.jackrabbit.name.QName; +import org.apache.jackrabbit.name.Path; + +import javax.jcr.RepositoryException; +import javax.jcr.PropertyType; +import javax.jcr.Value; +import javax.jcr.ValueFactory; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; + +/** + * ValueFormat... + */ +public class ValueFormat { + + public static QValue getQValue(Value jcrValue, NamespaceResolver nsResolver) throws RepositoryException { + if (jcrValue == null) { + throw new IllegalArgumentException("null value"); + } + if (jcrValue.getType() == PropertyType.BINARY) { + try { + return QValue.create(jcrValue.getStream()); + } catch (IOException e) { + throw new RepositoryException(e); + } + } else { + return getQValue(jcrValue.getString(), jcrValue.getType(), nsResolver); + } + } + + public static QValue[] getQValues(Value[] jcrValues, NamespaceResolver nsResolver) throws RepositoryException { + if (jcrValues == null) { + throw new IllegalArgumentException("null value"); + } + List qValues = new ArrayList(); + for (int i = 0; i < jcrValues.length; i++) { + if (jcrValues[i] != null) { + qValues.add(getQValue(jcrValues[i], nsResolver)); + } + } + return (QValue[]) qValues.toArray(new QValue[qValues.size()]); + } + + public static QValue getQValue(String jcrValue, int propertyType, NamespaceResolver nsResolver) throws RepositoryException { + QValue qValue; + switch (propertyType) { + case PropertyType.STRING: + case PropertyType.BOOLEAN: + case PropertyType.DOUBLE: + case PropertyType.LONG: + case PropertyType.DATE: + case PropertyType.REFERENCE: + qValue = QValue.create(jcrValue, propertyType); + break; + case PropertyType.BINARY: + qValue = QValue.create(jcrValue.getBytes()); + break; + case PropertyType.NAME: + try { + QName qName = nsResolver.getQName(jcrValue); + qValue = QValue.create(qName); + } catch (NameException e) { + throw new RepositoryException(e); + } + break; + case PropertyType.PATH: + try { + Path qPath = nsResolver.getQPath(jcrValue).getNormalizedPath(); + qValue = QValue.create(qPath); + } catch (NameException e) { + throw new RepositoryException(e); + } + break; + default: + throw new IllegalArgumentException("Invalid property type."); + } + return qValue; + } + + /** + * @param nsResolver + * @return + * @throws RepositoryException + */ + public static Value getJCRValue(QValue qualifiedValue, + NamespaceResolver nsResolver, + ValueFactory factory) + throws RepositoryException { + Value jcrValue; + int propertyType = qualifiedValue.getType(); + switch (propertyType) { + case PropertyType.STRING: + case PropertyType.BOOLEAN: + case PropertyType.DATE: + case PropertyType.DOUBLE: + case PropertyType.LONG: + case PropertyType.REFERENCE: + jcrValue = factory.createValue(qualifiedValue.getString(), propertyType); + break; + case PropertyType.PATH: + try { + Path qPath = Path.valueOf(qualifiedValue.getString()); + jcrValue = factory.createValue(nsResolver.getJCRPath(qPath), propertyType); + } catch (NoPrefixDeclaredException npde) { + // should never get here... + throw new RepositoryException("internal error: encountered unregistered namespace", npde); + } + break; + case PropertyType.NAME: + try { + QName qName = QName.valueOf(qualifiedValue.getString()); + jcrValue = factory.createValue(nsResolver.getJCRName(qName), propertyType); + } catch (NoPrefixDeclaredException npde) { + // should never get here... + throw new RepositoryException("internal error: encountered unregistered namespace", npde); + } + break; + case PropertyType.BINARY: + jcrValue = factory.createValue(qualifiedValue.getStream()); + break; + default: + throw new RepositoryException("illegal internal value type"); + } + return jcrValue; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/ValueFormat.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/contrib/spi/commons/src/main/java/org/apache/jackrabbit/value/ValueFormat.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url Added: jackrabbit/trunk/contrib/spi/jcr-spi.ipr URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr-spi.ipr?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr-spi.ipr (added) +++ jackrabbit/trunk/contrib/spi/jcr-spi.ipr Wed Jul 12 06:33:19 2006 @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Added: jackrabbit/trunk/contrib/spi/jcr2spi/maven.xml URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/maven.xml?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/maven.xml (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/maven.xml Wed Jul 12 06:33:19 2006 @@ -0,0 +1,27 @@ + + + + + + + + Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/maven.xml ------------------------------------------------------------------------------ svn:eol-style = native Added: jackrabbit/trunk/contrib/spi/jcr2spi/project.properties URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/project.properties?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/project.properties (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/project.properties Wed Jul 12 06:33:19 2006 @@ -0,0 +1,2 @@ +maven.javadoc.links=http://java.sun.com/j2se/1.4.2/docs/api/,http://www.day.com/maven/jsr170/javadocs/jcr-0.16.1-pfd/ +maven.repo.remote = http://www.ibiblio.org/maven/,http://www.day.com/maven/ Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/project.properties ------------------------------------------------------------------------------ svn = Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/project.properties ------------------------------------------------------------------------------ svn:eol-style = native Added: jackrabbit/trunk/contrib/spi/jcr2spi/project.xml URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/project.xml?rev=421270&view=auto ============================================================================== --- jackrabbit/trunk/contrib/spi/jcr2spi/project.xml (added) +++ jackrabbit/trunk/contrib/spi/jcr2spi/project.xml Wed Jul 12 06:33:19 2006 @@ -0,0 +1,102 @@ + + + + + + + ${basedir}/../project.xml + jackrabbit-spi-jcr2spi + JCR to SPI + org.apache.jackrabbit.jcr2spi.* + + + + + + + org.apache.jackrabbit + jackrabbit-spi + ${jackrabbit.build.version.spi} + + + org.apache.jackrabbit + jackrabbit-spi-commons + ${jackrabbit.build.version.spi} + + + + org.apache.jackrabbit + jackrabbit-jcr-commons + ${jackrabbit.build.version.jackrabbit} + + + + jsr170 + jcr + ${jackrabbit.build.version.jcr} + http://jcp.org/en/jsr/detail?id=170 + + + org.slf4j + slf4j-log4j12 + 1.0 + http://www.slf4j.org/download.html + + + commons-collections + commons-collections + 3.1 + http://jakarta.apache.org/commons/collections/ + + + + junit + junit + 3.8.1 + http://www.junit.org/ + + test + + + + + + + + + ${basedir}/src/main/java + src/test/java + + + **/*Test.java + + + + + src/main/java + + **/*.xml + **/*.xsd + **/*.properties + **/*.dtd + + + + + + Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/project.xml ------------------------------------------------------------------------------ svn:eol-style = native