From 7a62f8dc50516d0e14f4bdefc01cbb3a8c9fad7d Mon Sep 17 00:00:00 2001
From: Dmitriy Shabanov
Date: Wed, 30 Apr 2014 13:53:46 +0400
Subject: [PATCH 1/5] Change API to allow construction
try ( Transaction tx = broker.beginTx() )
{
// operations on database
tx.success();
}
---
src/org/exist/Transaction.java | 33 +++++++++++++++++++
src/org/exist/storage/DBBroker.java | 10 ++++--
.../exist/storage/txn/TransactionManager.java | 2 +-
src/org/exist/storage/txn/Txn.java | 27 ++++++++++++---
src/template_java.txt | 2 +-
5 files changed, 64 insertions(+), 10 deletions(-)
create mode 100644 src/org/exist/Transaction.java
diff --git a/src/org/exist/Transaction.java b/src/org/exist/Transaction.java
new file mode 100644
index 00000000000..9f82733fc5a
--- /dev/null
+++ b/src/org/exist/Transaction.java
@@ -0,0 +1,33 @@
+/*
+ * eXist Open Source Native XML Database
+ * Copyright (C) 2001-2014 The eXist Project
+ * http://exist-db.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package org.exist;
+
+import org.exist.storage.txn.TransactionException;
+
+/**
+ * @author Dmitriy Shabanov
+ *
+ */
+public interface Transaction extends AutoCloseable {
+
+ public void success() throws TransactionException;
+
+ public void failure();
+}
diff --git a/src/org/exist/storage/DBBroker.java b/src/org/exist/storage/DBBroker.java
index c9a2fa214f8..f89284a53e2 100644
--- a/src/org/exist/storage/DBBroker.java
+++ b/src/org/exist/storage/DBBroker.java
@@ -839,7 +839,11 @@ public abstract EmbeddedXMLStreamReader newXMLStreamReader(NodeHandle node, bool
public abstract void readCollectionEntry(SubCollectionEntry entry);
- public void release() {
- pool.release(this);
- }
+ public void release() {
+ pool.release(this);
+ }
+
+ public Txn beginTx() {
+ return getDatabase().getTransactionManager().beginTransaction();
+ }
}
diff --git a/src/org/exist/storage/txn/TransactionManager.java b/src/org/exist/storage/txn/TransactionManager.java
index 206db98232d..7dc69cb80db 100644
--- a/src/org/exist/storage/txn/TransactionManager.java
+++ b/src/org/exist/storage/txn/TransactionManager.java
@@ -157,7 +157,7 @@ public Txn beginTransaction() {
public Txn execute() {
final long txnId = nextTxnId++;
LOG.debug("Starting new transaction: " + txnId);
- final Txn txn = new Txn(txnId);
+ final Txn txn = new Txn(TransactionManager.this, txnId);
try {
journal.writeToLog(new TxnStart(txnId));
} catch (final TransactionException e) {
diff --git a/src/org/exist/storage/txn/Txn.java b/src/org/exist/storage/txn/Txn.java
index 711d5cdd066..f43e914d49b 100644
--- a/src/org/exist/storage/txn/Txn.java
+++ b/src/org/exist/storage/txn/Txn.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.List;
+import org.exist.Transaction;
import org.exist.storage.lock.Lock;
import org.exist.util.LockException;
@@ -31,7 +32,7 @@
* @author wolf
*
*/
-public class Txn {
+public class Txn implements Transaction {
public enum State { STARTED, ABORTED, COMMITTED };
@@ -44,8 +45,11 @@ public enum State { STARTED, ABORTED, COMMITTED };
private List listeners = new ArrayList();
private String originId;
+
+ private TransactionManager tm;
- public Txn(long transactionId) {
+ public Txn(TransactionManager tm, long transactionId) {
+ this.tm = tm;
this.id = transactionId;
this.state = State.STARTED;
}
@@ -54,7 +58,7 @@ public State getState() {
return state;
}
- public void setState(State state) {
+ protected void setState(State state) {
this.state = state;
}
@@ -83,14 +87,14 @@ public void registerListener(TxnListener listener) {
listeners.add(listener);
}
- public void signalAbort() {
+ protected void signalAbort() {
state = State.ABORTED;
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).abort();
}
}
- public void signalCommit() {
+ protected void signalCommit() {
state = State.COMMITTED;
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).commit();
@@ -124,4 +128,17 @@ public String getOriginId() {
public void setOriginId(String id) {
originId = id;
}
+
+ public void success() throws TransactionException {
+ tm.commit(this);
+ }
+
+ public void failure() {
+ tm.abort(this);
+ }
+
+ @Override
+ public void close() {
+ tm.close(this);
+ }
}
\ No newline at end of file
diff --git a/src/template_java.txt b/src/template_java.txt
index c4c753a24ef..7ccb0954d97 100644
--- a/src/template_java.txt
+++ b/src/template_java.txt
@@ -1,6 +1,6 @@
/*
* eXist Open Source Native XML Database
- * Copyright (C) 2014 The eXist Project
+ * Copyright (C) 2001-2014 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
From 57488337625564319421d7d8173d12e14f8905a6 Mon Sep 17 00:00:00 2001
From: Dmitriy Shabanov
Date: Wed, 30 Apr 2014 13:53:46 +0400
Subject: [PATCH 2/5] Change API to allow construction
try ( DBBroker broker = db.authenticate("user", "passwd") )
{
// operations on database
DBBroker activeBroker = db.getActiveBroker();
//broker == activeBroker
}
---
src/org/exist/Transaction.java | 33 +++++++++++++++++++
src/org/exist/storage/DBBroker.java | 18 +++++++---
.../exist/storage/txn/TransactionManager.java | 2 +-
src/org/exist/storage/txn/Txn.java | 27 ++++++++++++---
src/template_java.txt | 2 +-
5 files changed, 71 insertions(+), 11 deletions(-)
create mode 100644 src/org/exist/Transaction.java
diff --git a/src/org/exist/Transaction.java b/src/org/exist/Transaction.java
new file mode 100644
index 00000000000..9f82733fc5a
--- /dev/null
+++ b/src/org/exist/Transaction.java
@@ -0,0 +1,33 @@
+/*
+ * eXist Open Source Native XML Database
+ * Copyright (C) 2001-2014 The eXist Project
+ * http://exist-db.org
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+package org.exist;
+
+import org.exist.storage.txn.TransactionException;
+
+/**
+ * @author Dmitriy Shabanov
+ *
+ */
+public interface Transaction extends AutoCloseable {
+
+ public void success() throws TransactionException;
+
+ public void failure();
+}
diff --git a/src/org/exist/storage/DBBroker.java b/src/org/exist/storage/DBBroker.java
index c9a2fa214f8..d686b65d55d 100644
--- a/src/org/exist/storage/DBBroker.java
+++ b/src/org/exist/storage/DBBroker.java
@@ -56,6 +56,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Observable;
+
import org.exist.collections.Collection.SubCollectionEntry;
/**
@@ -65,7 +66,7 @@
*
* @author Wolfgang Meier
*/
-public abstract class DBBroker extends Observable {
+public abstract class DBBroker extends Observable implements AutoCloseable {
// Matching types
public final static int MATCH_EXACT = 0;
@@ -839,7 +840,16 @@ public abstract EmbeddedXMLStreamReader newXMLStreamReader(NodeHandle node, bool
public abstract void readCollectionEntry(SubCollectionEntry entry);
- public void release() {
- pool.release(this);
- }
+ public void close() throws Exception {
+ pool.release(this);
+ }
+
+ @Deprecated //use close() method instead
+ public void release() {
+ pool.release(this);
+ }
+
+ public Txn beginTx() {
+ return getDatabase().getTransactionManager().beginTransaction();
+ }
}
diff --git a/src/org/exist/storage/txn/TransactionManager.java b/src/org/exist/storage/txn/TransactionManager.java
index 206db98232d..7dc69cb80db 100644
--- a/src/org/exist/storage/txn/TransactionManager.java
+++ b/src/org/exist/storage/txn/TransactionManager.java
@@ -157,7 +157,7 @@ public Txn beginTransaction() {
public Txn execute() {
final long txnId = nextTxnId++;
LOG.debug("Starting new transaction: " + txnId);
- final Txn txn = new Txn(txnId);
+ final Txn txn = new Txn(TransactionManager.this, txnId);
try {
journal.writeToLog(new TxnStart(txnId));
} catch (final TransactionException e) {
diff --git a/src/org/exist/storage/txn/Txn.java b/src/org/exist/storage/txn/Txn.java
index 711d5cdd066..f43e914d49b 100644
--- a/src/org/exist/storage/txn/Txn.java
+++ b/src/org/exist/storage/txn/Txn.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.List;
+import org.exist.Transaction;
import org.exist.storage.lock.Lock;
import org.exist.util.LockException;
@@ -31,7 +32,7 @@
* @author wolf
*
*/
-public class Txn {
+public class Txn implements Transaction {
public enum State { STARTED, ABORTED, COMMITTED };
@@ -44,8 +45,11 @@ public enum State { STARTED, ABORTED, COMMITTED };
private List listeners = new ArrayList();
private String originId;
+
+ private TransactionManager tm;
- public Txn(long transactionId) {
+ public Txn(TransactionManager tm, long transactionId) {
+ this.tm = tm;
this.id = transactionId;
this.state = State.STARTED;
}
@@ -54,7 +58,7 @@ public State getState() {
return state;
}
- public void setState(State state) {
+ protected void setState(State state) {
this.state = state;
}
@@ -83,14 +87,14 @@ public void registerListener(TxnListener listener) {
listeners.add(listener);
}
- public void signalAbort() {
+ protected void signalAbort() {
state = State.ABORTED;
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).abort();
}
}
- public void signalCommit() {
+ protected void signalCommit() {
state = State.COMMITTED;
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).commit();
@@ -124,4 +128,17 @@ public String getOriginId() {
public void setOriginId(String id) {
originId = id;
}
+
+ public void success() throws TransactionException {
+ tm.commit(this);
+ }
+
+ public void failure() {
+ tm.abort(this);
+ }
+
+ @Override
+ public void close() {
+ tm.close(this);
+ }
}
\ No newline at end of file
diff --git a/src/template_java.txt b/src/template_java.txt
index c4c753a24ef..7ccb0954d97 100644
--- a/src/template_java.txt
+++ b/src/template_java.txt
@@ -1,6 +1,6 @@
/*
* eXist Open Source Native XML Database
- * Copyright (C) 2014 The eXist Project
+ * Copyright (C) 2001-2014 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
From 18c620af7aacdb76701c2f212d6fa3146194739d Mon Sep 17 00:00:00 2001
From: Dmitriy Shabanov
Date: Wed, 30 Apr 2014 15:44:01 +0400
Subject: [PATCH 3/5] Revert "Merge branch 'develop' of
https://github.com/shabanovd/exist into"
This reverts commit 46013f7134dbeaf9f2142109ce0447c13192cc41, reversing
changes made to 57488337625564319421d7d8173d12e14f8905a6.
---
backup.properties | 36 +-
bin/backup.bat | 128 +-
bin/client.bat | 114 +-
bin/run.bat | 120 +-
bin/server.bat | 116 +-
bin/shutdown.bat | 110 +-
bin/startup.bat | 120 +-
build.properties | 158 +-
build.xml | 70 +-
build/scripts/demoserver.xml | 592 +-
build/scripts/dist-war-conf.xsl | 52 +-
build/scripts/dist-war-log4j.xsl | 42 +-
build/scripts/dist.xml | 660 +-
build/scripts/installer.xml | 440 +-
build/scripts/jarsigner.xml | 346 +-
build/scripts/quality.xml | 248 +-
build/scripts/soap.xml | 228 +-
client.properties.tmpl | 108 +-
descriptor.xml.tmpl | 140 +-
.../betterform/main/etc/MergeWebXML.xsl | 318 +-
.../org/exist/fluent/AttributeBuilder.java | 302 +-
.../src/org/exist/fluent/DataUtils.java | 238 +-
.../fluent/src/org/exist/fluent/Database.java | 1312 +--
.../org/exist/fluent/DatabaseException.java | 58 +-
.../fluent/src/org/exist/fluent/Document.java | 764 +-
.../src/org/exist/fluent/ElementBuilder.java | 640 +-
.../fluent/src/org/exist/fluent/Folder.java | 2424 ++---
.../fluent/src/org/exist/fluent/Item.java | 666 +-
.../fluent/src/org/exist/fluent/ItemList.java | 818 +-
.../fluent/src/org/exist/fluent/Listener.java | 134 +-
.../fluent/src/org/exist/fluent/Name.java | 472 +-
.../src/org/exist/fluent/NamedResource.java | 532 +-
.../src/org/exist/fluent/NamespaceMap.java | 442 +-
.../fluent/src/org/exist/fluent/QName.java | 312 +-
.../src/org/exist/fluent/QueryService.java | 1674 ++--
.../fluent/src/org/exist/fluent/Resource.java | 154 +-
.../fluent/src/org/exist/fluent/Source.java | 848 +-
.../src/org/exist/fluent/StaleMarker.java | 46 +-
.../src/org/exist/fluent/Transaction.java | 174 +-
.../exist/fluent/WeakMultiValueHashMap.java | 242 +-
.../src/org/exist/fluent/XMLDocument.java | 268 +-
.../fluent/src/org/exist/fluent/package.html | 250 +-
.../org/exist/fluent/DatabaseTestCase.java | 168 +-
.../src/org/exist/fluent/DocumentTest.java | 140 +-
.../src/org/exist/fluent/MetadataTest.java | 238 +-
.../exist/http/servlets/ScaleImageJAI.java | 914 +-
.../src/xquery/lucene/FT_AttTest_complex.xml | 590 +-
.../LuceneFT+range_indexRetrievalTest.xml | 380 +-
.../src/xquery/lucene/indirectQueriesTest.xml | 876 +-
.../test/src/xquery/lucene/startOffset.xml | 326 +-
.../org/exist/indexing/ngram/NGramIndex.java | 240 +-
.../indexing/ngram/NGramIndexConfig.java | 128 +-
.../indexing/ngram/NGramIndexWorker.java | 2236 ++---
.../xquery/modules/ngram/NGramModule.java | 150 +-
.../xquery/modules/ngram/NGramSearch.java | 1252 +--
.../exist/indexing/ngram/AllIndexTests.java | 24 +-
.../exist/indexing/ngram/CustomIndexTest.java | 1468 +--
.../indexing/ngram/MatchListenerTest.java | 1460 +--
extensions/indexes/spatial/hsql.bat | 4 +-
.../spatial/AbstractGMLJDBCIndex.java | 412 +-
.../spatial/AbstractGMLJDBCIndexWorker.java | 1696 ++--
.../exist/indexing/spatial/GMLHSQLIndex.java | 602 +-
.../indexing/spatial/GMLHSQLIndexWorker.java | 1540 ++--
.../indexing/spatial/GMLIndexConfig.java | 108 +-
.../spatial/SpatialIndexException.java | 74 +-
.../modules/spatial/FunGMLProducers.java | 870 +-
.../spatial/FunGeometricProperties.java | 812 +-
.../modules/spatial/FunSpatialSearch.java | 350 +-
.../xquery/modules/spatial/SpatialModule.java | 222 +-
.../indexing/spatial/AllSpatialTests.java | 68 +-
.../exist/indexing/spatial/GMLIndexTest.java | 2208 ++---
extensions/metadata/interface/pom.xml | 12 +-
.../exist/storage/md/CollectionEvents.java | 306 +-
.../org/exist/storage/md/DocumentEvents.java | 246 +-
.../exist/storage/md/MDStorageManager.java | 606 +-
.../exist/storage/md/BackupRestoreMDTest.java | 628 +-
.../exist/storage/md/DocumentAsValueTest.java | 458 +-
.../exist/storage/md/MatchDocumentsTest.java | 1412 +--
.../java/org/exist/storage/md/SearchTest.java | 666 +-
.../org/exist/storage/md/SimpleMDTest.java | 2074 ++---
extensions/metadata/sleepycat/pom.xml | 28 +-
.../org/exist/storage/md/MetaDataImpl.java | 1192 +--
.../java/org/exist/storage/md/MetaImpl.java | 204 +-
.../java/org/exist/storage/md/MetasImpl.java | 292 +-
.../modules/cache/CacheBasicFunction.java | 146 +-
.../xquery/modules/cache/CacheFunction.java | 138 +-
.../xquery/modules/cache/CacheModule.java | 154 +-
.../xquery/modules/cache/ClearFunction.java | 182 +-
.../xquery/modules/cache/GetFunction.java | 164 +-
.../xquery/modules/cache/PutFunction.java | 168 +-
.../xquery/modules/cache/RemoveFunction.java | 164 +-
.../compression/AbstractCompressFunction.java | 1016 +--
.../compression/CompressionModule.java | 164 +-
.../modules/compression/GZipFunction.java | 188 +-
.../modules/compression/TarFunction.java | 200 +-
.../modules/compression/UnGZipFunction.java | 196 +-
.../modules/compression/ZipFunction.java | 204 +-
.../modules/compression/example-untar.xql | 64 +-
.../modules/compression/example-unzip.xql | 64 +-
.../xquery/modules/context/ContextModule.java | 154 +-
.../modules/datetime/DayInWeekFunction.java | 138 +-
.../modules/datetime/DaysInMonthFunction.java | 146 +-
.../modules/datetime/FormatDateFunction.java | 154 +-
.../datetime/FormatDateTimeFunction.java | 158 +-
.../modules/datetime/FormatTimeFunction.java | 156 +-
.../datetime/TimeFromDateTimeFunction.java | 130 +-
.../modules/datetime/WeekInMonthFunction.java | 148 +-
.../exist/xquery/modules/exi/EXIUtils.java | 116 +-
.../xquery/modules/exi/EncodeExiFunction.java | 244 +-
.../exist/xquery/modules/exi/ExiModule.java | 140 +-
.../exist/xquery/modules/file/FileDelete.java | 188 +-
.../exist/xquery/modules/file/FileExists.java | 188 +-
.../xquery/modules/file/FileIsDirectory.java | 188 +-
.../xquery/modules/file/FileIsReadable.java | 188 +-
.../xquery/modules/file/FileIsWriteable.java | 188 +-
.../exist/xquery/modules/file/FileRead.java | 270 +-
.../xquery/modules/file/FileReadBinary.java | 200 +-
.../xquery/modules/file/FileReadUnicode.java | 274 +-
.../xquery/modules/file/SerializeToFile.java | 564 +-
.../xquery/modules/file/UnicodeReader.java | 356 +-
.../exist/xquery/modules/jndi/jndiExample.xql | 226 +-
.../mail/access_imap_mailbox_example.xql | 78 +-
.../modules/mail/send-email_sendmail.xql | 82 +-
.../xquery/modules/mail/send-email_smtp.xql | 82 +-
.../exist/xquery/modules/math/MathModule.java | 170 +-
.../xquery/modules/math/NoParamFunctions.java | 232 +-
.../modules/math/OneParamFunctions.java | 476 +-
.../modules/math/TwoParamFunctions.java | 244 +-
.../modules/oracle/ExecuteFunction.java | 876 +-
.../xquery/modules/oracle/OracleModule.java | 166 +-
.../xquery/modules/oracle/oracle-example.xql | 70 +-
.../exist/xquery/modules/oracle/readme.txt | 32 +-
.../modules/scheduler/SchedulerModule.java | 188 +-
.../xquery/modules/simpleql/SimpleQLParser.g | 1606 ++--
.../exist/xquery/modules/sql/SQLUtils.java | 366 +-
.../org/exist/xquery/modules/xslfo/readme.txt | 28 +-
extensions/scheduler/pom.xml | 28 +-
.../main/java/org/exist/scheduler/Job.java | 258 +-
.../org/exist/scheduler/SchedulerManager.java | 282 +-
.../security/realm/oauth/Google2Api.java | 124 +-
.../svn/internal/wc/admin/SVNAdminArea16.java | 478 +-
.../wc/admin/SVNAdminArea16Factory.java | 106 +-
.../svn/xquery/XQueryFunctionsTest.java | 686 +-
extensions/webdav/lib/milton-LICENSE.txt | 104 +-
.../src/org/exist/webdav/MiltonDocument.java | 1196 +--
.../org/exist/xquery/xUnit/Annotations.java | 172 +-
.../org/exist/xquery/xUnit/AssertEquals.java | 110 +-
.../java/org/exist/xquery/xUnit/xUnit.java | 272 +-
.../test/java/org/exist/xquery/xUnit/test.xql | 18 +-
extensions/xprocxq/main/src/xquery/ant.xqm | 32 +-
extensions/xprocxq/main/src/xquery/const.xqm | 268 +-
extensions/xprocxq/main/src/xquery/ext.xqm | 134 +-
.../xprocxq/main/src/xquery/functions.xqm | 128 +-
extensions/xprocxq/main/src/xquery/naming.xqm | 690 +-
extensions/xprocxq/main/src/xquery/opt.xqm | 324 +-
extensions/xprocxq/main/src/xquery/std.xqm | 1088 +--
extensions/xprocxq/main/src/xquery/util.xqm | 2076 ++---
extensions/xprocxq/main/src/xquery/xproc.xqm | 2108 ++---
extensions/xqdoc/ant-example.xml | 76 +-
extensions/xqdoc/xqdoc-1.0.xsd | 378 +-
.../org/exist/interpreter/ContextAtExist.java | 128 +-
.../xslt/src/org/exist/xslt/SourceImpl.java | 178 +-
.../org/exist/xslt/TemplatesHandlerImpl.java | 160 +-
.../exist/xslt/TransformerFactoryImpl.java | 546 +-
.../exist/xslt/TransformerHandlerImpl.java | 180 +-
.../src/org/exist/xslt/TransformerImpl.java | 548 +-
extensions/xslt/src/org/exist/xslt/XSL.java | 324 +-
.../xslt/src/org/exist/xslt/XSLContext.java | 266 +-
.../src/org/exist/xslt/XSLExceptions.java | 1740 ++--
.../src/org/exist/xslt/XSLStylesheet.java | 936 +-
.../src/org/exist/xslt/compiler/Factory.java | 202 +-
.../src/org/exist/xslt/compiler/Names.java | 294 +-
.../org/exist/xslt/compiler/XSLElement.java | 1468 +--
.../exist/xslt/expression/AnalyzeString.java | 236 +-
.../exist/xslt/expression/ApplyImports.java | 160 +-
.../exist/xslt/expression/ApplyTemplates.java | 448 +-
.../org/exist/xslt/expression/Attribute.java | 604 +-
.../exist/xslt/expression/AttributeSet.java | 272 +-
.../exist/xslt/expression/CallTemplate.java | 316 +-
.../exist/xslt/expression/CharacterMap.java | 212 +-
.../src/org/exist/xslt/expression/Choose.java | 230 +-
.../org/exist/xslt/expression/Comment.java | 354 +-
.../src/org/exist/xslt/expression/Copy.java | 410 +-
.../src/org/exist/xslt/expression/CopyOf.java | 452 +-
.../exist/xslt/expression/DecimalFormat.java | 402 +-
.../exist/xslt/expression/Declaration.java | 92 +-
.../org/exist/xslt/expression/Document.java | 244 +-
.../org/exist/xslt/expression/Element.java | 804 +-
.../org/exist/xslt/expression/Fallback.java | 160 +-
.../org/exist/xslt/expression/ForEach.java | 362 +-
.../exist/xslt/expression/ForEachGroup.java | 578 +-
.../org/exist/xslt/expression/Function.java | 234 +-
.../src/org/exist/xslt/expression/If.java | 232 +-
.../src/org/exist/xslt/expression/Import.java | 186 +-
.../exist/xslt/expression/ImportSchema.java | 216 +-
.../org/exist/xslt/expression/Include.java | 186 +-
.../src/org/exist/xslt/expression/Key.java | 258 +-
.../xslt/expression/MatchingSubstring.java | 160 +-
.../org/exist/xslt/expression/Message.java | 224 +-
.../org/exist/xslt/expression/Namespace.java | 214 +-
.../exist/xslt/expression/NamespaceAlias.java | 210 +-
.../org/exist/xslt/expression/NextMatch.java | 160 +-
.../xslt/expression/NonMatchingSubstring.java | 160 +-
.../src/org/exist/xslt/expression/Number.java | 408 +-
.../org/exist/xslt/expression/Otherwise.java | 160 +-
.../src/org/exist/xslt/expression/Output.java | 560 +-
.../xslt/expression/OutputCharacter.java | 204 +-
.../src/org/exist/xslt/expression/Param.java | 588 +-
.../exist/xslt/expression/PerformSort.java | 192 +-
.../exist/xslt/expression/PreserveSpace.java | 186 +-
.../expression/ProcessingInstruction.java | 392 +-
.../exist/xslt/expression/ResultDocument.java | 608 +-
.../xslt/expression/SequenceConstructor.java | 392 +-
.../xslt/expression/SimpleConstructor.java | 136 +-
.../src/org/exist/xslt/expression/Sort.java | 468 +-
.../org/exist/xslt/expression/StripSpace.java | 186 +-
.../org/exist/xslt/expression/Template.java | 742 +-
.../src/org/exist/xslt/expression/Text.java | 346 +-
.../org/exist/xslt/expression/ValueOf.java | 562 +-
.../org/exist/xslt/expression/Variable.java | 356 +-
.../src/org/exist/xslt/expression/When.java | 250 +-
.../org/exist/xslt/expression/WithParam.java | 348 +-
.../exist/xslt/expression/XSLExpression.java | 192 +-
.../exist/xslt/expression/XSLPathExpr.java | 406 +-
.../exist/xslt/expression/i/Parameted.java | 80 +-
.../src/org/exist/xslt/functions/Current.java | 130 +-
.../org/exist/xslt/functions/Document.java | 150 +-
.../org/exist/xslt/functions/Format_date.java | 220 +-
.../exist/xslt/functions/Format_dateTime.java | 204 +-
.../exist/xslt/functions/Format_number.java | 214 +-
.../org/exist/xslt/functions/Format_time.java | 200 +-
.../org/exist/xslt/functions/Generate_id.java | 142 +-
.../src/org/exist/xslt/functions/Key.java | 154 +-
.../exist/xslt/functions/System_property.java | 176 +-
.../functions/Unparsed_entity_public_id.java | 132 +-
.../xslt/functions/Unparsed_entity_uri.java | 132 +-
.../exist/xslt/functions/Unparsed_text.java | 150 +-
.../functions/Unparsed_text_available.java | 150 +-
.../org/exist/xslt/functions/XSLModule.java | 202 +-
.../src/org/exist/xslt/pattern/Pattern.java | 360 +-
.../test/src/org/exist/xslt/XSLTestCase.java | 1034 +--
installer/launch_client.bat | 44 +-
installer/scripts/setup.bat | 42 +-
lib/core/antlr-LICENSE.txt | 56 +-
lib/core/clj-ds-LICENSE.html | 522 +-
lib/core/commons-codec-LICENSE.txt | 404 +-
lib/core/commons-collections-LICENSE.txt | 404 +-
lib/core/commons-collections-NOTICE.txt | 10 +-
lib/core/commons-io-LICENSE.txt | 406 +-
lib/core/commons-io-NOTICE.txt | 12 +-
lib/core/commons-logging-LICENSE.txt | 404 +-
lib/core/commons-logging-NOTICE.txt | 12 +-
lib/core/excalibur-cli-LICENSE.txt | 100 +-
lib/core/slf4j-LICENSE.txt | 48 +-
lib/endorsed/saxonhe-LICENSE.txt | 28 +-
lib/endorsed/serializer-LICENSE.txt | 402 +-
lib/endorsed/serializer-NOTICE.txt | 36 +-
lib/endorsed/xalan-LICENSE.txt | 1368 +--
lib/endorsed/xalan-NOTICE.txt | 160 +-
lib/endorsed/xercesImpl-LICENSE.txt | 404 +-
lib/endorsed/xercesImpl-NOTICE.txt | 32 +-
lib/endorsed/xml-apis-LICENSE.txt | 404 +-
lib/endorsed/xml-apis-NOTICE.txt | 32 +-
lib/endorsed/xml-resolver-LICENSE.txt | 402 +-
lib/endorsed/xml-resolver-NOTICE.txt | 18 +-
lib/optional/commons-fileupload-LICENSE.txt | 404 +-
lib/optional/commons-fileupload-NOTICE.txt | 10 +-
lib/optional/commons-httpclient-LICENSE.txt | 352 +-
lib/optional/commons-httpclient-NOTICE.txt | 10 +-
lib/optional/commons-lang-LICENSE.txt | 404 +-
lib/optional/commons-net-LICENSE.txt | 404 +-
lib/optional/isorelax-LICENSE.txt | 14 +-
lib/optional/jing-LICENSE.txt | 62 +-
lib/optional/servlet-api-LICENSE.txt | 404 +-
lib/optional/servlet-api-NOTICE.txt | 4 +-
lib/user/exificient-LICENSE.txt | 678 +-
log4j.xml | 540 +-
mime-types.xml.tmpl | 710 +-
samples/ant/ant-test.xml | 80 +-
samples/build.xml | 128 +-
.../examples/xmlrpc/RetrieveChunked.java | 236 +-
.../exist/examples/xmlrpc/StoreChunked.java | 226 +-
samples/validation/addressbook/catalog.xml | 8 +-
.../personal/personal-dtd-invalid.xml | 104 +-
samples/validation/personal/personal-dtd.xml | 104 +-
.../validation/personal/personal-invalid.xml | 102 +-
.../validation/personal/personal-relaxng.xml | 104 +-
.../validation/personal/personal-valid.xml | 104 +-
.../personal-xsd_noSchemaLocation-invalid.xml | 104 +-
.../personal-xsd_noSchemaLocation.xml | 104 +-
samples/validation/personal/personal.rnc | 110 +-
samples/validation/personal/personal.rng | 362 +-
.../tournament/1.5/Tournament-invalid.xml | 92 +-
.../tournament/1.5/Tournament-valid.xml | 92 +-
.../validation/tournament/1.5/Tournament.rng | 194 +-
.../validation/tournament/1.5/Tournament.xsd | 230 +-
.../tournament/1.5/tournament-schema.sch | 120 +-
.../validation/tournament/iso/Tournament.rng | 206 +-
.../validation/tournament/iso/Tournament.xml | 92 +-
.../validation/tournament/iso/Tournament.xsd | 240 +-
.../tournament/iso/tournament-schema.sch | 160 +-
samples/xinclude/styles/SyntaxHighlighter.css | 370 +-
samples/xinclude/styles/niftyCorners.css | 68 +-
samples/xinclude/styles/niftycube.js | 594 +-
samples/xquery/backup.xq | 18 +-
src/org/exist/EventListener.java | 64 +-
src/org/exist/backup/BackupHandler.java | 82 +-
src/org/exist/backup/BackupWriter.java | 122 +-
src/org/exist/backup/FileSystemWriter.java | 284 +-
src/org/exist/backup/RestoreHandler.java | 80 +-
src/org/exist/backup/SystemImport.java | 322 +-
src/org/exist/backup/ZipWriter.java | 298 +-
src/org/exist/client/Messages.java | 86 +-
src/org/exist/client/PrettyXmldbURI.java | 84 +-
src/org/exist/client/messages.properties | 592 +-
.../exist/client/messages_es_ES.properties | 730 +-
.../exist/client/messages_fr_FR.properties | 588 +-
.../exist/client/messages_it_IT.properties | 634 +-
.../exist/client/messages_nl_NL.properties | 574 +-
src/org/exist/client/messages_no.properties | 422 +-
.../exist/client/messages_ru_RU.properties | 528 +-
src/org/exist/dom/AttrAtExist.java | 66 +-
src/org/exist/dom/DocumentAtExist.java | 136 +-
src/org/exist/dom/ElementAtExist.java | 74 +-
src/org/exist/dom/MutableDocumentSet.java | 82 +-
src/org/exist/dom/NamespaceNodeAtExist.java | 62 +-
src/org/exist/dom/NodeAtExist.java | 90 +-
src/org/exist/dom/NodeHandle.java | 42 +-
src/org/exist/fulltext/FTMatchListener.java | 444 +-
src/org/exist/http/run-xproc.xq | 54 +-
src/org/exist/indexing/AbstractIndex.java | 174 +-
.../exist/indexing/AbstractMatchListener.java | 248 +-
.../indexing/AbstractStreamListener.java | 162 +-
src/org/exist/indexing/Index.java | 244 +-
src/org/exist/indexing/IndexController.java | 826 +-
src/org/exist/indexing/IndexManager.java | 482 +-
src/org/exist/indexing/IndexUtils.java | 120 +-
src/org/exist/indexing/IndexWorker.java | 436 +-
src/org/exist/indexing/MatchListener.java | 72 +-
.../exist/indexing/OrderedValuesIndex.java | 44 +-
src/org/exist/indexing/QNamedKeysIndex.java | 34 +-
src/org/exist/indexing/StreamListener.java | 228 +-
src/org/exist/installer/Setup.java | 404 +-
.../exist/management/client/JMXClient.java | 846 +-
.../eXistURLStreamHandlerFactory.java | 202 +-
.../embedded/EmbeddedDownload.java | 328 +-
.../embedded/EmbeddedInputStream.java | 238 +-
.../embedded/EmbeddedOutputStream.java | 180 +-
.../embedded/EmbeddedUser.java | 142 +-
.../protocolhandler/embedded/package.html | 8 +-
.../protocols/xmldb/Connection.java | 214 +-
.../protocols/xmldb/package.html | 8 +-
.../exist/protocolhandler/xmldb/XmldbURL.java | 738 +-
.../exist/protocolhandler/xmldb/package.html | 8 +-
.../xmlrpc/XmlrpcDownload.java | 234 +-
.../xmlrpc/XmlrpcDownloadThread.java | 154 +-
.../xmlrpc/XmlrpcInputStream.java | 190 +-
.../xmlrpc/XmlrpcOutputStream.java | 172 +-
.../protocolhandler/xmlrpc/XmlrpcUpload.java | 264 +-
.../xmlrpc/XmlrpcUploadThread.java | 134 +-
.../exist/protocolhandler/xmlrpc/package.html | 8 +-
src/org/exist/scheduler/JobDescription.java | 102 +-
src/org/exist/scheduler/JobException.java | 162 +-
src/org/exist/scheduler/ScheduledJobInfo.java | 374 +-
src/org/exist/scheduler/UserJavaJob.java | 152 +-
src/org/exist/scheduler/UserJob.java | 78 +-
src/org/exist/scheduler/UserXQueryJob.java | 530 +-
.../scheduler/impl/SystemTaskJobImpl.java | 234 +-
src/org/exist/security/UUIDGenerator.java | 142 +-
.../internal/EventAuthentication.java | 138 +-
src/org/exist/security/internal/SMEvents.java | 384 +-
src/org/exist/soap/admin.wsdl | 1072 +--
src/org/exist/soap/deployAdmin.wsdd | 246 +-
src/org/exist/soap/deployQuery.wsdd | 182 +-
src/org/exist/soap/query.wsdl | 1232 +--
src/org/exist/soap/undeployAdmin.wsdd | 30 +-
src/org/exist/soap/undeployQuery.wsdd | 30 +-
.../exist/source/StringSourceWithMapKey.java | 96 +-
src/org/exist/start/LatestFileResolver.java | 226 +-
.../exist/stax/EmbeddedXMLStreamReader.java | 1372 +--
src/org/exist/storage/BackupSystemTask.java | 334 +-
src/org/exist/storage/DBBroker.java | 1 -
.../storage/btree/SetPageLinkLoggable.java | 154 +-
src/org/exist/storage/btree/TreeMetrics.java | 148 +-
.../exist/storage/dom/RawNodeIterator.java | 448 +-
src/org/exist/storage/dom/RecordPos.java | 128 +-
.../exist/storage/io/BlockingInputStream.java | 764 +-
.../storage/io/BlockingOutputStream.java | 262 +-
.../exist/storage/lock/FileLockHeartBeat.java | 154 +-
src/org/exist/storage/lock/LockOwner.java | 102 +-
.../exist/storage/lock/LockedDocumentMap.java | 226 +-
src/org/exist/storage/md/Meta.java | 76 +-
src/org/exist/storage/md/MetaData.java | 172 +-
src/org/exist/storage/md/Metas.java | 90 +-
src/org/exist/storage/sync/Sync.java | 68 +-
src/org/exist/util/Base64Decoder.java | 472 +-
src/org/exist/util/Base64Encoder.java | 298 +-
.../exist/util/FloatingPointConverter.java | 1296 +--
.../util/SingleInstanceConfiguration.java | 278 +-
src/org/exist/util/hashtable/MapRWLock.java | 348 +-
.../exist/util/io/TemporaryFileManager.java | 302 +-
src/org/exist/util/pool/NodePool.java | 242 +-
.../exist/util/serializer/EXISerializer.java | 404 +-
.../validation/ValidationReportItem.java | 232 +-
.../exist/validation/XmlLibraryChecker.java | 736 +-
.../internal/DatabaseResources.java | 442 +-
.../internal/node/NodeInputStream.java | 176 +-
.../internal/node/NodeSerializer.java | 168 +-
.../internal/node/NodeSerializerThread.java | 168 +-
.../internal/query/find_catalogs_with_dtd.xq | 28 +-
.../query/find_schema_by_targetNamespace.xq | 26 +-
.../validation/resolver/AnyUriResolver.java | 304 +-
.../resolver/SearchResourceResolver.java | 328 +-
.../resolver/eXistXMLCatalogResolver.java | 470 +-
src/org/exist/webstart/package.html | 10 +-
src/org/exist/xmldb/ExtendedResource.java | 130 +-
src/org/exist/xquery/AnnotationTrigger.java | 60 +-
.../xquery/AnnotationTriggerOnResult.java | 68 +-
src/org/exist/xquery/Annotations.java | 116 +-
.../xquery/DefaultExpressionVisitor.java | 326 +-
src/org/exist/xquery/GroupSpec.java | 178 +-
src/org/exist/xquery/IndexUseReporter.java | 76 +-
src/org/exist/xquery/Optimizer.java | 736 +-
.../xquery/functions/fn/FnFormatNumbers.java | 816 +-
.../functions/fn/FunCodepointEqual.java | 186 +-
.../xquery/functions/fn/FunDateTime.java | 274 +-
.../functions/fn/FunDefaultCollation.java | 146 +-
.../exist/xquery/functions/fn/FunEquals.java | 258 +-
.../functions/fn/FunGetDurationComponent.java | 338 +-
.../exist/xquery/functions/fn/FunIdRef.java | 414 +-
.../exist/xquery/functions/fn/FunNilled.java | 202 +-
.../exist/xquery/functions/fn/FunTrace.java | 278 +-
.../xquery/functions/math/MathModule.java | 164 +-
.../functions/math/NoParamFunctions.java | 196 +-
.../functions/math/OneParamFunctions.java | 430 +-
.../functions/math/TwoParamFunctions.java | 250 +-
.../xquery/functions/system/GetRevision.java | 124 +-
.../functions/system/TriggerSystemTask.java | 254 +-
.../exist/xquery/functions/text/Tokenize.java | 156 +-
.../functions/util/DeepCopyFunction.java | 178 +-
.../xquery/functions/util/FunDoctype.java | 192 +-
.../functions/util/GetSequenceType.java | 126 +-
.../functions/util/IndexKeyDocuments.java | 250 +-
.../functions/util/IndexKeyOccurrences.java | 254 +-
.../xquery/functions/util/IndexType.java | 210 +-
src/org/exist/xquery/functions/util/UUID.java | 190 +-
src/org/exist/xquery/functions/util/Wait.java | 148 +-
.../functions/xmldb/FunXCollection.java | 120 +-
.../xquery/functions/xmldb/XMLDBDocument.java | 532 +-
src/org/exist/xquery/modules/ModuleUtils.java | 958 +-
.../exist/xquery/pragmas/ForceIndexUse.java | 146 +-
.../exist/xquery/pragmas/ProfilePragma.java | 92 +-
src/org/exist/xquery/regex/CaseVariants.java | 3776 ++++----
src/org/exist/xquery/regex/IntRangeSet.java | 804 +-
.../xquery/regex/JDK15RegexTranslator.java | 2032 ++---
src/org/exist/xquery/regex/RegexData.java | 722 +-
.../xquery/regex/RegexSyntaxException.java | 106 +-
.../exist/xquery/regex/XMLCharacterData.java | 1570 ++--
.../xquery/value/GroupedValueSequence.java | 530 +-
.../value/GroupedValueSequenceTable.java | 238 +-
.../exist/backup/SystemExportImportTest.java | 564 +-
.../org/exist/config/mapping/MappedClass.java | 96 +-
test/src/org/exist/config/mapping/Mapping.xml | 24 +-
.../org/exist/config/mapping/SubConfig.java | 96 +-
.../deadlocks/GetReleaseBrokerDeadlocks.java | 410 +-
test/src/org/exist/dom/BasicNodeSetTest.java | 1014 +--
test/src/org/exist/dom/utf8.xml | 12 +-
test/src/org/exist/http/RESTTest.java | 72 +-
.../performance/actions/XUpdateAction.java | 106 +-
test/src/org/exist/performance/test.xml | 1038 +--
.../storage/RemoveRootCollectionTest.java | 158 +-
.../org/exist/storage/btree/BTreeTest.java | 1094 +--
test/src/org/exist/test/TestConstants.java | 206 +-
test/src/org/exist/util/MimeTableTest.java | 302 +-
test/src/org/exist/util/SortTests.java | 146 +-
.../org/exist/util/mime-types-foo-default.xml | 18 +-
.../org/exist/util/mime-types-xml-default.xml | 18 +-
.../exist/util/sorters/ComparatorChecker.java | 166 +-
.../exist/util/sorters/FastQSortTester.java | 204 +-
.../org/exist/util/sorters/HSortTester.java | 188 +-
.../exist/util/sorters/HeapSortTester.java | 204 +-
.../util/sorters/InsertionSortTester.java | 200 +-
.../org/exist/util/sorters/ListChecker.java | 180 +-
.../sorters/LongArrayAndObjectChecker.java | 186 +-
.../util/sorters/NodeProxyByIdChecker.java | 168 +-
.../exist/util/sorters/NodeProxyChecker.java | 172 +-
.../sorters/ObjectAndIntArrayChecker.java | 190 +-
.../exist/util/sorters/PlainArrayChecker.java | 312 +-
.../exist/util/sorters/SortMethodChecker.java | 206 +-
.../org/exist/util/sorters/SortTestCase.java | 382 +-
.../util/sorters/SortTestComparator.java | 354 +-
.../exist/util/sorters/SortTestNodeId.java | 298 +-
.../exist/util/sorters/SortTestNodeProxy.java | 124 +-
.../util/sorters/SortingAlgorithmTester.java | 156 +-
.../validation/DatabaseCollectionTest.java | 216 +-
test/src/org/exist/validation/TestTools.java | 338 +-
.../ValidationFunctions_Node_Test.java | 516 +-
.../ValidationFunctions_XSD_Test.java | 634 +-
test/src/org/exist/xmldb/CollectionTest.java | 144 +-
.../org/exist/xmldb/SerializationTest.java | 266 +-
.../org/exist/xmlrpc/MoveResourceTest.java | 466 +-
.../org/exist/xmlrpc/QuerySessionTest.java | 364 +-
.../org/exist/xquery/JavaFunctionsTest.java | 198 +-
test/src/org/exist/xquery/OptimizerTest.java | 618 +-
.../functions/request/GetHeaderTest.java | 182 +-
.../functions/request/GetParameterTest.java | 1026 +--
.../functions/util/Base64FunctionsTest.java | 280 +-
.../xquery/functions/util/ExpandTest.java | 216 +-
.../org/exist/xquery/value/AnyURITest.java | 132 +-
.../rename_including_namespace.xml | 36 +-
.../modifications/rename_root_element.xml | 24 +-
test/src/xquery/matchHighlighting.xml | 376 +-
test/src/xquery/namespaces.xml | 126 +-
test/src/xquery/parenthesizedContext.xml | 1090 +--
test/src/xquery/parenthesizedLocationStep.xml | 1110 +--
...arenthesizedLocationStep_ftquery_Tests.xml | 358 +-
test/src/xquery/pathExpression_operators.xml | 72 +-
test/src/xquery/selfAxis.xml | 362 +-
test/src/xquery/unionOperator.xml | 192 +-
tools/ant/LICENSE.txt | 544 +-
tools/ant/lib/asocat-exist.LICENSE.txt | 330 +-
tools/ant/lib/jdepend-LICENSE.txt | 58 +-
tools/backrest/build.xml | 192 +-
tools/backrest/src/backup.bat | 108 +-
tools/backrest/src/backup.properties | 36 +-
tools/ircbot/build.xml | 114 +-
tools/ircbot/run.bat | 14 +-
tools/ircbot/src/org/exist/irc/IRCProxy.java | 292 +-
.../ircbot/src/org/exist/irc/IRCSession.java | 804 +-
tools/ircbot/src/org/exist/irc/XBot.java | 924 +-
tools/ircbot/webapp/WEB-INF/web.xml | 54 +-
.../org/exist/izpack/PasswordValidator.java | 72 +-
tools/rulesets/basic.xml | 2006 ++---
tools/rulesets/migrating.xml | 356 +-
tools/rulesets/optimizations.xml | 626 +-
tools/rulesets/scratchpad.xml | 188 +-
tools/wrapper/bin/exist.bat | 270 +-
tools/wrapper/bin/install.bat | 270 +-
tools/wrapper/bin/uninstall.bat | 266 +-
tools/wrapper/wrapper-log4j.xsl | 36 +-
webapp/WEB-INF/entities/play.xsd | 280 +-
webapp/WEB-INF/entities/xml.xsd | 266 +-
webapp/xqts/hacked-tests.xml | 32 +-
webapp/xqts/scripts/container.js | 7834 ++++++++---------
webapp/xqts/scripts/dom.js | 1774 ++--
webapp/xqts/scripts/event.js | 2400 ++---
webapp/xqts/scripts/shBrushXQuery.js | 158 +-
webapp/xqts/scripts/shBrushXml.js | 122 +-
webapp/xqts/scripts/yahoo.js | 168 +-
webapp/xqts/skip.xml | 6 +-
webapp/xqts/styles/SyntaxHighlighter.css | 502 +-
webapp/xqts/styles/container.css | 420 +-
552 files changed, 99753 insertions(+), 99754 deletions(-)
diff --git a/backup.properties b/backup.properties
index ef444b5115c..a1413caa395 100644
--- a/backup.properties
+++ b/backup.properties
@@ -1,18 +1,18 @@
-# properties used for backup/restore
-
-# XMLDB driver
-driver=org.exist.xmldb.DatabaseImpl
-
-# uncomment following line if you want to access a
-# stand-alone server
-#uri=xmldb:exist://localhost:8088/xmlrpc
-
-# access eXist via XML-RPC provided by remote Tomcat
-uri=xmldb:exist://localhost:8080/exist/xmlrpc
-
-# access a local instance of the database
-#uri=xmldb:exist://
-
-# user settings for backup/restore
-user=admin
-password=
+# properties used for backup/restore
+
+# XMLDB driver
+driver=org.exist.xmldb.DatabaseImpl
+
+# uncomment following line if you want to access a
+# stand-alone server
+#uri=xmldb:exist://localhost:8088/xmlrpc
+
+# access eXist via XML-RPC provided by remote Tomcat
+uri=xmldb:exist://localhost:8080/exist/xmlrpc
+
+# access a local instance of the database
+#uri=xmldb:exist://
+
+# user settings for backup/restore
+user=admin
+password=
diff --git a/bin/backup.bat b/bin/backup.bat
index 6ad0a3a8cf2..6fc19397c97 100644
--- a/bin/backup.bat
+++ b/bin/backup.bat
@@ -1,64 +1,64 @@
-@echo off
-
-rem $Id$
-
-rem Slurp the command line arguments. This loop allows for an unlimited number
-rem of arguments (up to the command line limit, anyway).
-
-set CMD_LINE_ARGS=%1
-if ""%1""=="""" goto doneStart
-shift
-:setupArgs
-if ""%1""=="""" goto doneStart
-set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
-shift
-goto setupArgs
-
-rem This label provides a place for the argument list loop to break out
-rem and for NT handling to skip to.
-
-:doneStart
-
-set JAVA_RUN="java"
-
-if not "%JAVA_HOME%" == "" (
- set JAVA_RUN="%JAVA_HOME%\bin\java"
- goto gotJavaHome
-)
-
-rem @WINDOWS_INSTALLER_1@
-
-echo WARNING: JAVA_HOME not found in your environment.
-echo.
-echo Please, set the JAVA_HOME variable in your enviroment to match the
-echo location of the Java Virtual Machine you want to use in case of run fail.
-echo.
-
-:gotJavaHome
-if not "%EXIST_HOME%" == "" goto gotExistHome
-
-rem try to guess (will be overridden by the installer)
-set EXIST_HOME=.
-
-rem @WINDOWS_INSTALLER_2@
-
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-set EXIST_HOME=..
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-echo EXIST_HOME not found. Please set your
-echo EXIST_HOME environment variable to the
-echo home directory of eXist.
-goto :eof
-
-:gotExistHome
-set MX=768
-rem @WINDOWS_INSTALLER_3@
-
-set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
-set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
-
-%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" backup %CMD_LINE_ARGS%
-:eof
-
+@echo off
+
+rem $Id$
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of arguments (up to the command line limit, anyway).
+
+set CMD_LINE_ARGS=%1
+if ""%1""=="""" goto doneStart
+shift
+:setupArgs
+if ""%1""=="""" goto doneStart
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setupArgs
+
+rem This label provides a place for the argument list loop to break out
+rem and for NT handling to skip to.
+
+:doneStart
+
+set JAVA_RUN="java"
+
+if not "%JAVA_HOME%" == "" (
+ set JAVA_RUN="%JAVA_HOME%\bin\java"
+ goto gotJavaHome
+)
+
+rem @WINDOWS_INSTALLER_1@
+
+echo WARNING: JAVA_HOME not found in your environment.
+echo.
+echo Please, set the JAVA_HOME variable in your enviroment to match the
+echo location of the Java Virtual Machine you want to use in case of run fail.
+echo.
+
+:gotJavaHome
+if not "%EXIST_HOME%" == "" goto gotExistHome
+
+rem try to guess (will be overridden by the installer)
+set EXIST_HOME=.
+
+rem @WINDOWS_INSTALLER_2@
+
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+set EXIST_HOME=..
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+echo EXIST_HOME not found. Please set your
+echo EXIST_HOME environment variable to the
+echo home directory of eXist.
+goto :eof
+
+:gotExistHome
+set MX=768
+rem @WINDOWS_INSTALLER_3@
+
+set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
+set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
+
+%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" backup %CMD_LINE_ARGS%
+:eof
+
diff --git a/bin/client.bat b/bin/client.bat
index ee1c6f52357..0aadadabbeb 100644
--- a/bin/client.bat
+++ b/bin/client.bat
@@ -1,57 +1,57 @@
-@echo off
-rem $Id$
-rem
-rem In addition to the other parameter options for the interactive client
-rem pass -j or --jmx to enable JMX agent. The port for it can be specified
-rem with optional port number e.g. -j1099 or --jmx=1099.
-rem
-
-set JMX_ENABLED=0
-set JMX_PORT=1099
-set JAVA_ARGS=
-
-set JAVA_RUN="java"
-
-if not "%JAVA_HOME%" == "" (
- set JAVA_RUN="%JAVA_HOME%\bin\java"
- goto gotJavaHome
-)
-
-rem @WINDOWS_INSTALLER_1@
-
-echo WARNING: JAVA_HOME not found in your environment.
-echo.
-echo Please, set the JAVA_HOME variable in your enviroment to match the
-echo location of the Java Virtual Machine you want to use in case of run fail.
-echo.
-
-:gotJavaHome
-rem @WINDOWS_INSTALLER_2@
-
-if not "%EXIST_HOME%" == "" goto gotExistHome
-
-rem try to guess (will be set by the installer)
-set EXIST_HOME=.
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-set EXIST_HOME=..
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-echo EXIST_HOME not found. Please set your
-echo EXIST_HOME environment variable to the
-echo home directory of eXist.
-goto :eof
-
-:gotExistHome
-set MX=1024
-rem @WINDOWS_INSTALLER_3@
-
-set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
-set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
-
-set BATCH.D="%EXIST_HOME%\bin\batch.d"
-call %BATCH.D%\get_opts.bat %*
-call %BATCH.D%\check_jmx_status.bat
-
-%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" client %JAVA_ARGS%
-:eof
+@echo off
+rem $Id$
+rem
+rem In addition to the other parameter options for the interactive client
+rem pass -j or --jmx to enable JMX agent. The port for it can be specified
+rem with optional port number e.g. -j1099 or --jmx=1099.
+rem
+
+set JMX_ENABLED=0
+set JMX_PORT=1099
+set JAVA_ARGS=
+
+set JAVA_RUN="java"
+
+if not "%JAVA_HOME%" == "" (
+ set JAVA_RUN="%JAVA_HOME%\bin\java"
+ goto gotJavaHome
+)
+
+rem @WINDOWS_INSTALLER_1@
+
+echo WARNING: JAVA_HOME not found in your environment.
+echo.
+echo Please, set the JAVA_HOME variable in your enviroment to match the
+echo location of the Java Virtual Machine you want to use in case of run fail.
+echo.
+
+:gotJavaHome
+rem @WINDOWS_INSTALLER_2@
+
+if not "%EXIST_HOME%" == "" goto gotExistHome
+
+rem try to guess (will be set by the installer)
+set EXIST_HOME=.
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+set EXIST_HOME=..
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+echo EXIST_HOME not found. Please set your
+echo EXIST_HOME environment variable to the
+echo home directory of eXist.
+goto :eof
+
+:gotExistHome
+set MX=1024
+rem @WINDOWS_INSTALLER_3@
+
+set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
+set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
+
+set BATCH.D="%EXIST_HOME%\bin\batch.d"
+call %BATCH.D%\get_opts.bat %*
+call %BATCH.D%\check_jmx_status.bat
+
+%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" client %JAVA_ARGS%
+:eof
diff --git a/bin/run.bat b/bin/run.bat
index 7ac853b7965..91d98182b8f 100644
--- a/bin/run.bat
+++ b/bin/run.bat
@@ -1,61 +1,61 @@
-@echo off
-
-rem $Id$
-
-rem Slurp the command line arguments. This loop allows for an unlimited number
-rem of arguments (up to the command line limit, anyway).
-
-set CMD_LINE_ARGS=%1
-if ""%1""=="""" goto doneStart
-shift
-:setupArgs
-if ""%1""=="""" goto doneStart
-set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
-shift
-goto setupArgs
-
-rem This label provides a place for the argument list loop to break out
-rem and for NT handling to skip to.
-
-:doneStart
-set JAVA_RUN="java"
-
-if not "%JAVA_HOME%" == "" (
- set JAVA_RUN="%JAVA_HOME%\bin\java"
- goto gotJavaHome
-)
-
-rem @WINDOWS_INSTALLER_1@
-
-echo WARNING: JAVA_HOME not found in your environment.
-echo.
-echo Please, set the JAVA_HOME variable in your enviroment to match the
-echo location of the Java Virtual Machine you want to use in case of run fail.
-echo.
-
-:gotJavaHome
-rem @WINDOWS_INSTALLER_2@
-
-if not "%EXIST_HOME%" == "" goto gotExistHome
-
-rem try to guess
-set EXIST_HOME=.
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-set EXIST_HOME=..
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-echo EXIST_HOME not found. Please set your
-echo EXIST_HOME environment variable to the
-echo home directory of eXist.
-goto :eof
-
-:gotExistHome
-set MX=768
-rem @WINDOWS_INSTALLER_3@
-
-set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
-set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
-
-:gotJavaOpts
-%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" %CMD_LINE_ARGS%
+@echo off
+
+rem $Id$
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of arguments (up to the command line limit, anyway).
+
+set CMD_LINE_ARGS=%1
+if ""%1""=="""" goto doneStart
+shift
+:setupArgs
+if ""%1""=="""" goto doneStart
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setupArgs
+
+rem This label provides a place for the argument list loop to break out
+rem and for NT handling to skip to.
+
+:doneStart
+set JAVA_RUN="java"
+
+if not "%JAVA_HOME%" == "" (
+ set JAVA_RUN="%JAVA_HOME%\bin\java"
+ goto gotJavaHome
+)
+
+rem @WINDOWS_INSTALLER_1@
+
+echo WARNING: JAVA_HOME not found in your environment.
+echo.
+echo Please, set the JAVA_HOME variable in your enviroment to match the
+echo location of the Java Virtual Machine you want to use in case of run fail.
+echo.
+
+:gotJavaHome
+rem @WINDOWS_INSTALLER_2@
+
+if not "%EXIST_HOME%" == "" goto gotExistHome
+
+rem try to guess
+set EXIST_HOME=.
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+set EXIST_HOME=..
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+echo EXIST_HOME not found. Please set your
+echo EXIST_HOME environment variable to the
+echo home directory of eXist.
+goto :eof
+
+:gotExistHome
+set MX=768
+rem @WINDOWS_INSTALLER_3@
+
+set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
+set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
+
+:gotJavaOpts
+%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" %CMD_LINE_ARGS%
:eof
\ No newline at end of file
diff --git a/bin/server.bat b/bin/server.bat
index de9c4f50f95..649cd4dda59 100644
--- a/bin/server.bat
+++ b/bin/server.bat
@@ -1,58 +1,58 @@
-@echo off
-
-rem $Id$
-rem
-rem In addition to the other parameter options for the standalone server
-rem pass -j or --jmx to enable JMX agent. The port for it can be specified
-rem with optional port number e.g. -j1099 or --jmx=1099.
-rem
-
-set JMX_ENABLED=0
-set JMX_PORT=1099
-set JAVA_ARGS=
-
-set JAVA_RUN="java"
-
-if not "%JAVA_HOME%" == "" (
- set JAVA_RUN="%JAVA_HOME%\bin\java"
- goto gotJavaHome
-)
-
-rem @WINDOWS_INSTALLER_1@
-
-echo WARNING: JAVA_HOME not found in your environment.
-echo.
-echo Please, set the JAVA_HOME variable in your environment to match the
-echo location of the Java Virtual Machine you want to use in case of run fail.
-echo.
-
-:gotJavaHome
-rem @WINDOWS_INSTALLER_2@
-
-if not "%EXIST_HOME%" == "" goto gotExistHome
-
-rem try to guess (will be set by the installer)
-set EXIST_HOME=.
-
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-set EXIST_HOME=..
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-echo EXIST_HOME not found. Please set your
-echo EXIST_HOME environment variable to the
-echo home directory of eXist.
-goto :eof
-
-:gotExistHome
-set MX=1024
-rem @WINDOWS_INSTALLER_3@
-
-set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
-set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
-
-set BATCH.D="%EXIST_HOME%\bin\batch.d"
-call %BATCH.D%\get_opts.bat %*
-call %BATCH.D%\check_jmx_status.bat
-
-%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" standalone %JAVA_ARGS%
-:eof
+@echo off
+
+rem $Id$
+rem
+rem In addition to the other parameter options for the standalone server
+rem pass -j or --jmx to enable JMX agent. The port for it can be specified
+rem with optional port number e.g. -j1099 or --jmx=1099.
+rem
+
+set JMX_ENABLED=0
+set JMX_PORT=1099
+set JAVA_ARGS=
+
+set JAVA_RUN="java"
+
+if not "%JAVA_HOME%" == "" (
+ set JAVA_RUN="%JAVA_HOME%\bin\java"
+ goto gotJavaHome
+)
+
+rem @WINDOWS_INSTALLER_1@
+
+echo WARNING: JAVA_HOME not found in your environment.
+echo.
+echo Please, set the JAVA_HOME variable in your environment to match the
+echo location of the Java Virtual Machine you want to use in case of run fail.
+echo.
+
+:gotJavaHome
+rem @WINDOWS_INSTALLER_2@
+
+if not "%EXIST_HOME%" == "" goto gotExistHome
+
+rem try to guess (will be set by the installer)
+set EXIST_HOME=.
+
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+set EXIST_HOME=..
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+echo EXIST_HOME not found. Please set your
+echo EXIST_HOME environment variable to the
+echo home directory of eXist.
+goto :eof
+
+:gotExistHome
+set MX=1024
+rem @WINDOWS_INSTALLER_3@
+
+set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
+set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
+
+set BATCH.D="%EXIST_HOME%\bin\batch.d"
+call %BATCH.D%\get_opts.bat %*
+call %BATCH.D%\check_jmx_status.bat
+
+%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" standalone %JAVA_ARGS%
+:eof
diff --git a/bin/shutdown.bat b/bin/shutdown.bat
index 04176241435..7b092ad21e7 100644
--- a/bin/shutdown.bat
+++ b/bin/shutdown.bat
@@ -1,56 +1,56 @@
-@echo off
-
-rem $Id$
-
-rem Slurp the command line arguments. This loop allows for an unlimited number
-rem of arguments (up to the command line limit, anyway).
-
-set CMD_LINE_ARGS=%1
-if ""%1""=="""" goto doneStart
-shift
-:setupArgs
-if ""%1""=="""" goto doneStart
-set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
-shift
-goto setupArgs
-
-rem This label provides a place for the argument list loop to break out
-rem and for NT handling to skip to.
-
-:doneStart
-
-set JAVA_RUN="java"
-
-if not "%JAVA_HOME%" == "" (
- set JAVA_RUN="%JAVA_HOME%\bin\java"
- goto gotJavaHome
-)
-
-rem @WINDOWS_INSTALLER_1@
-
-echo WARNING: JAVA_HOME not found in your environment.
-echo.
-echo Please, set the JAVA_HOME variable in your enviroment to match the
-echo location of the Java Virtual Machine you want to use in case of run fail.
-echo.
-
-:gotJavaHome
-rem @WINDOWS_INSTALLER_2@
-
-if not "%EXIST_HOME%" == "" goto gotExistHome
-
-rem try to guess (will be set by the installer)
-set EXIST_HOME=.
-
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-set EXIST_HOME=..
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-echo EXIST_HOME not found. Please set your
-echo EXIST_HOME environment variable to the
-echo home directory of eXist.
-goto :eof
-
-:gotExistHome
-%JAVA_RUN% -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" shutdown %CMD_LINE_ARGS%
+@echo off
+
+rem $Id$
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of arguments (up to the command line limit, anyway).
+
+set CMD_LINE_ARGS=%1
+if ""%1""=="""" goto doneStart
+shift
+:setupArgs
+if ""%1""=="""" goto doneStart
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto setupArgs
+
+rem This label provides a place for the argument list loop to break out
+rem and for NT handling to skip to.
+
+:doneStart
+
+set JAVA_RUN="java"
+
+if not "%JAVA_HOME%" == "" (
+ set JAVA_RUN="%JAVA_HOME%\bin\java"
+ goto gotJavaHome
+)
+
+rem @WINDOWS_INSTALLER_1@
+
+echo WARNING: JAVA_HOME not found in your environment.
+echo.
+echo Please, set the JAVA_HOME variable in your enviroment to match the
+echo location of the Java Virtual Machine you want to use in case of run fail.
+echo.
+
+:gotJavaHome
+rem @WINDOWS_INSTALLER_2@
+
+if not "%EXIST_HOME%" == "" goto gotExistHome
+
+rem try to guess (will be set by the installer)
+set EXIST_HOME=.
+
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+set EXIST_HOME=..
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+echo EXIST_HOME not found. Please set your
+echo EXIST_HOME environment variable to the
+echo home directory of eXist.
+goto :eof
+
+:gotExistHome
+%JAVA_RUN% -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" shutdown %CMD_LINE_ARGS%
:eof
\ No newline at end of file
diff --git a/bin/startup.bat b/bin/startup.bat
index 30f59cf7c6b..5a51fe8e72f 100644
--- a/bin/startup.bat
+++ b/bin/startup.bat
@@ -1,60 +1,60 @@
-@echo off
-
-rem $Id$
-rem
-rem In addition to the other parameter options for the Jetty container
-rem pass -j or --jmx to enable JMX agent. The port for it can be specified
-rem with optional port number e.g. -j1099 or --jmx=1099.
-rem
-
-set JMX_ENABLED=0
-set JMX_PORT=1099
-set JAVA_ARGS=
-
-set JAVA_RUN="java"
-
-if not "%JAVA_HOME%" == "" (
- set JAVA_RUN="%JAVA_HOME%\bin\java"
- goto gotJavaHome
-)
-
-rem @WINDOWS_INSTALLER_1@
-
-echo WARNING: JAVA_HOME not found in your environment.
-echo.
-echo Please, set the JAVA_HOME variable in your enviroment to match the
-echo location of the Java Virtual Machine you want to use in case of run fail.
-echo.
-
-:gotJavaHome
-rem @WINDOWS_INSTALLER_2@
-
-if not "%EXIST_HOME%" == "" goto gotExistHome
-
-rem try to guess
-set EXIST_HOME=.
-
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-set EXIST_HOME=..
-if exist "%EXIST_HOME%\start.jar" goto gotExistHome
-
-echo EXIST_HOME not found. Please set your
-echo EXIST_HOME environment variable to the
-echo home directory of eXist.
-goto :eof
-
-:gotExistHome
-
-set MX=1024
-rem @WINDOWS_INSTALLER_3@
-
-set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
-set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
-
-set BATCH.D="%EXIST_HOME%\bin\batch.d"
-call %BATCH.D%\get_opts.bat %*
-call %BATCH.D%\check_jmx_status.bat
-
-%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" jetty %JAVA_ARGS%
-:eof
-
+@echo off
+
+rem $Id$
+rem
+rem In addition to the other parameter options for the Jetty container
+rem pass -j or --jmx to enable JMX agent. The port for it can be specified
+rem with optional port number e.g. -j1099 or --jmx=1099.
+rem
+
+set JMX_ENABLED=0
+set JMX_PORT=1099
+set JAVA_ARGS=
+
+set JAVA_RUN="java"
+
+if not "%JAVA_HOME%" == "" (
+ set JAVA_RUN="%JAVA_HOME%\bin\java"
+ goto gotJavaHome
+)
+
+rem @WINDOWS_INSTALLER_1@
+
+echo WARNING: JAVA_HOME not found in your environment.
+echo.
+echo Please, set the JAVA_HOME variable in your enviroment to match the
+echo location of the Java Virtual Machine you want to use in case of run fail.
+echo.
+
+:gotJavaHome
+rem @WINDOWS_INSTALLER_2@
+
+if not "%EXIST_HOME%" == "" goto gotExistHome
+
+rem try to guess
+set EXIST_HOME=.
+
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+set EXIST_HOME=..
+if exist "%EXIST_HOME%\start.jar" goto gotExistHome
+
+echo EXIST_HOME not found. Please set your
+echo EXIST_HOME environment variable to the
+echo home directory of eXist.
+goto :eof
+
+:gotExistHome
+
+set MX=1024
+rem @WINDOWS_INSTALLER_3@
+
+set JAVA_ENDORSED_DIRS="%EXIST_HOME%\lib\endorsed"
+set JAVA_OPTS="-Xms128m -Xmx%MX%m -Dfile.encoding=UTF-8 -Djava.endorsed.dirs=%JAVA_ENDORSED_DIRS%"
+
+set BATCH.D="%EXIST_HOME%\bin\batch.d"
+call %BATCH.D%\get_opts.bat %*
+call %BATCH.D%\check_jmx_status.bat
+
+%JAVA_RUN% "%JAVA_OPTS%" -Dexist.home="%EXIST_HOME%" -jar "%EXIST_HOME%\start.jar" jetty %JAVA_ARGS%
+:eof
+
diff --git a/build.properties b/build.properties
index da397ba37ad..f93f3823c2b 100644
--- a/build.properties
+++ b/build.properties
@@ -1,79 +1,79 @@
-#
-# Don't directly modify this file. Instead, copy it to local.build.properties and
-# edit that.
-#
-# $Id$
-project.name = eXist-db
-project.version = 2.1
-project.version.numeric = 2.1
-project.codename = Ruesselsheim
-
-# build settings
-build.debug = on
-build.optimize = on
-build.deprecation = off
-build.encoding = UTF-8
-build.compiler.pedantic = false
-build.compiler=modern
-build.compiler.source=1.6
-build.compiler.target=1.6
-
-autodeploy=dashboard,shared,eXide
-autodeploy.repo=http://demo.exist-db.org/exist/apps/public-repo
-use.autodeploy.feature=true
-
-# output dir for junit reports
-junit.reports = test
-junit.output = true
-junit.forked.VM.maxmemory = 300m
-# Converted junit tests from external testsuites
-# reuire more memory, e g
-# XSLTS requires 512m
-# XQTS requires more than 400m
-junit.forked.VM.maxmemory.external = 512m
-proxy.nonproxyhosts =
-proxy.host =
-proxy.port = 0
-proxy.password =
-proxy.user =
-proxy.socks.host =
-proxy.socks.port = 0
-#proxy.ntlm.domain =
-
-# Ant
-tools.ant = ./tools/ant
-
-#aspectj
-tools.aspectj = ./tools/aspectj
-
-# Common libs
-lib.core = ./lib/core
-lib.optional = ./lib/optional
-lib.endorsed = ./lib/endorsed
-lib.user = ./lib/user
-lib.extensions = ./lib/extensions
-lib.test = ./lib/test
-
-# antlr is only needed if you change the XPath/XQuery parser
-# set these properties to true to get a lot of debugging output
-antlr.traceParser = false
-antlr.traceLexer = false
-antlr.traceTreeWalker = false
-
-# IZPack is required to create the installer package.
-# We currently require IzPack 4.3.5. The Izpack 5.0 beta does NOT work.
-# If you change this property value for a reason,
-# please use a version indicator in directory name,
-# eg /izpack-dir-path/izpack-5.0-beta-11.
-# You might need to change PermSpace to atleast 84 MB eg -XX:MaxPermSize=84m
-# If you only want to point to your own izpack installation directory
-# add this in local.build.properties instead so you don't commit it by mistake.
-izpack.dir = /Applications/IzPack/
-
-# Launch4J is required to create the windows installer
-# If you change this property value for a reason,
-# please use a version indicator in directory name,
-# eg /launch4j-dir-path/launch4j-x.x.x.
-# If you only want to point to your own launch4j installation directory
-# add this in local.build.properties instead so you don't commit it by mistake.
-launch4j.dir = /Applications/launch4j/
+#
+# Don't directly modify this file. Instead, copy it to local.build.properties and
+# edit that.
+#
+# $Id$
+project.name = eXist-db
+project.version = 2.1
+project.version.numeric = 2.1
+project.codename = Ruesselsheim
+
+# build settings
+build.debug = on
+build.optimize = on
+build.deprecation = off
+build.encoding = UTF-8
+build.compiler.pedantic = false
+build.compiler=modern
+build.compiler.source=1.6
+build.compiler.target=1.6
+
+autodeploy=dashboard,shared,eXide
+autodeploy.repo=http://demo.exist-db.org/exist/apps/public-repo
+use.autodeploy.feature=true
+
+# output dir for junit reports
+junit.reports = test
+junit.output = true
+junit.forked.VM.maxmemory = 300m
+# Converted junit tests from external testsuites
+# reuire more memory, e g
+# XSLTS requires 512m
+# XQTS requires more than 400m
+junit.forked.VM.maxmemory.external = 512m
+proxy.nonproxyhosts =
+proxy.host =
+proxy.port = 0
+proxy.password =
+proxy.user =
+proxy.socks.host =
+proxy.socks.port = 0
+#proxy.ntlm.domain =
+
+# Ant
+tools.ant = ./tools/ant
+
+#aspectj
+tools.aspectj = ./tools/aspectj
+
+# Common libs
+lib.core = ./lib/core
+lib.optional = ./lib/optional
+lib.endorsed = ./lib/endorsed
+lib.user = ./lib/user
+lib.extensions = ./lib/extensions
+lib.test = ./lib/test
+
+# antlr is only needed if you change the XPath/XQuery parser
+# set these properties to true to get a lot of debugging output
+antlr.traceParser = false
+antlr.traceLexer = false
+antlr.traceTreeWalker = false
+
+# IZPack is required to create the installer package.
+# We currently require IzPack 4.3.5. The Izpack 5.0 beta does NOT work.
+# If you change this property value for a reason,
+# please use a version indicator in directory name,
+# eg /izpack-dir-path/izpack-5.0-beta-11.
+# You might need to change PermSpace to atleast 84 MB eg -XX:MaxPermSize=84m
+# If you only want to point to your own izpack installation directory
+# add this in local.build.properties instead so you don't commit it by mistake.
+izpack.dir = /Applications/IzPack/
+
+# Launch4J is required to create the windows installer
+# If you change this property value for a reason,
+# please use a version indicator in directory name,
+# eg /launch4j-dir-path/launch4j-x.x.x.
+# If you only want to point to your own launch4j installation directory
+# add this in local.build.properties instead so you don't commit it by mistake.
+launch4j.dir = /Applications/launch4j/
diff --git a/build.xml b/build.xml
index fabcecaa454..eee82a6c196 100644
--- a/build.xml
+++ b/build.xml
@@ -1,35 +1,35 @@
-
-
-
-
-
-
-
- eXist Open Source Native XML Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ eXist Open Source Native XML Database
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/demoserver.xml b/build/scripts/demoserver.xml
index db66eb53ce3..18ef690b51b 100644
--- a/build/scripts/demoserver.xml
+++ b/build/scripts/demoserver.xml
@@ -1,296 +1,296 @@
-
-
-
-
-
-
-
-
-
- Create HTML files for docs on http://exist-db.org
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Removed cocoon
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ Create HTML files for docs on http://exist-db.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Removed cocoon
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/dist-war-conf.xsl b/build/scripts/dist-war-conf.xsl
index 4c1256e1035..81e0caccec5 100644
--- a/build/scripts/dist-war-conf.xsl
+++ b/build/scripts/dist-war-conf.xsl
@@ -1,26 +1,26 @@
-
-
-
-
-
-
-
-
- data
-
-
-
- data
-
-
-
- catalog.xml
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ data
+
+
+
+ data
+
+
+
+ catalog.xml
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/dist-war-log4j.xsl b/build/scripts/dist-war-log4j.xsl
index 8f8ab6f23a0..3977f49a590 100644
--- a/build/scripts/dist-war-log4j.xsl
+++ b/build/scripts/dist-war-log4j.xsl
@@ -1,21 +1,21 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/dist.xml b/build/scripts/dist.xml
index bcef3217020..f3d2f4c438d 100644
--- a/build/scripts/dist.xml
+++ b/build/scripts/dist.xml
@@ -1,330 +1,330 @@
-
-
-
-
- Create eXist-db distribution
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Create eXist-db distribution
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/installer.xml b/build/scripts/installer.xml
index 59005a0ffd6..27b7ea52af8 100644
--- a/build/scripts/installer.xml
+++ b/build/scripts/installer.xml
@@ -1,220 +1,220 @@
-
-
-
-
-
-
-
- Build installer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ Build installer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/jarsigner.xml b/build/scripts/jarsigner.xml
index 4d7b52e76c7..b2c0e6f9e4f 100644
--- a/build/scripts/jarsigner.xml
+++ b/build/scripts/jarsigner.xml
@@ -1,173 +1,173 @@
-
-
-
-
- Sign jarfiles for eXist webstart application
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- This target has been deprecated, please use 'jnlp-all'
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+ Sign jarfiles for eXist webstart application
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This target has been deprecated, please use 'jnlp-all'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/quality.xml b/build/scripts/quality.xml
index b9141502547..0c4b4cec254 100644
--- a/build/scripts/quality.xml
+++ b/build/scripts/quality.xml
@@ -1,124 +1,124 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- basic
- codesize
- design
- unusedcode
-
- optimizations
- strictexception
- clone
- coupling
- finalizers
- junit
- strings
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ basic
+ codesize
+ design
+ unusedcode
+
+ optimizations
+ strictexception
+ clone
+ coupling
+ finalizers
+ junit
+ strings
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/scripts/soap.xml b/build/scripts/soap.xml
index 171b9d76520..fc87fba7fcc 100644
--- a/build/scripts/soap.xml
+++ b/build/scripts/soap.xml
@@ -1,115 +1,115 @@
-
-
-
-
-
-
-
-
-
- Build Axis SOAP interface
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ Build Axis SOAP interface
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client.properties.tmpl b/client.properties.tmpl
index 7d01210b02d..e3dbbb1d310 100644
--- a/client.properties.tmpl
+++ b/client.properties.tmpl
@@ -1,54 +1,54 @@
-#
-# $Id$
-#
-
-## Default Driver
-driver=org.exist.xmldb.DatabaseImpl
-
-## Configure XMLRPC URI
-## uncomment one "uri=" and one "ssl-enable=" line
-
-## Default XMLRPC (HTTP)
-uri=xmldb:exist://localhost:8080/exist/xmlrpc
-ssl-enable=false
-
-## Secure XMLRPC (HTTPS)
-#uri=xmldb:exist://localhost:8443/exist/xmlrpc
-#ssl-enable=true
-
-## URI for stand-alone server
-#uri=xmldb:exist://localhost:8088/xmlrpc
-#ssl-enable=false
-
-## Access a local instance ('embedded mode')
-#uri=xmldb:exist://
-
-## Alternate URIs (to be checked)
-alternate_uri_0=xmldb:exist://localhost:8080/exist/xmlrpc
-alternate_uri_1=xmldb:exist://localhost:8088/xmlrpc
-alternate_uri_2=xmldb:exist://
-
-## pretty-print XML
-indent=yes
-
-## default character encoding for display
-##
-## note: the actual encoding of the output depends on your system's
-## language setting. Java will convert all output to System.out into
-## the current default encoding.
-#encoding=ISO-8859-1
-encoding=UTF-8
-
-## use ANSI colors for collection listings
-colors=false
-
-## show user permissions (may slow down the client)
-permissions=true
-
-## should XInclude elements be processed when
-## serializing a document?
-expand-xincludes=yes
-
-## highlight matches in element and attribute values?
-## possible values are "none", "both", "elements", "attributes"
-highlight-matches=none
+#
+# $Id$
+#
+
+## Default Driver
+driver=org.exist.xmldb.DatabaseImpl
+
+## Configure XMLRPC URI
+## uncomment one "uri=" and one "ssl-enable=" line
+
+## Default XMLRPC (HTTP)
+uri=xmldb:exist://localhost:8080/exist/xmlrpc
+ssl-enable=false
+
+## Secure XMLRPC (HTTPS)
+#uri=xmldb:exist://localhost:8443/exist/xmlrpc
+#ssl-enable=true
+
+## URI for stand-alone server
+#uri=xmldb:exist://localhost:8088/xmlrpc
+#ssl-enable=false
+
+## Access a local instance ('embedded mode')
+#uri=xmldb:exist://
+
+## Alternate URIs (to be checked)
+alternate_uri_0=xmldb:exist://localhost:8080/exist/xmlrpc
+alternate_uri_1=xmldb:exist://localhost:8088/xmlrpc
+alternate_uri_2=xmldb:exist://
+
+## pretty-print XML
+indent=yes
+
+## default character encoding for display
+##
+## note: the actual encoding of the output depends on your system's
+## language setting. Java will convert all output to System.out into
+## the current default encoding.
+#encoding=ISO-8859-1
+encoding=UTF-8
+
+## use ANSI colors for collection listings
+colors=false
+
+## show user permissions (may slow down the client)
+permissions=true
+
+## should XInclude elements be processed when
+## serializing a document?
+expand-xincludes=yes
+
+## highlight matches in element and attribute values?
+## possible values are "none", "both", "elements", "attributes"
+highlight-matches=none
diff --git a/descriptor.xml.tmpl b/descriptor.xml.tmpl
index 4af8ad832c6..4a401207a60 100644
--- a/descriptor.xml.tmpl
+++ b/descriptor.xml.tmpl
@@ -1,70 +1,70 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/betterform/main/etc/MergeWebXML.xsl b/extensions/betterform/main/etc/MergeWebXML.xsl
index ac20cc01ec9..a9f91fb1797 100644
--- a/extensions/betterform/main/etc/MergeWebXML.xsl
+++ b/extensions/betterform/main/etc/MergeWebXML.xsl
@@ -1,159 +1,159 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ====================== betterFORM configuration file ======================
-
-
-
- betterform.configfile
- WEB-INF/betterform-config.xml
-
-
-
-
-
-
-
-
- ====================== betterFORM filter and servlets ======================
-
-
-
- ====================== betterFORM servlets ======================
-
-
-
- Flux
-
- org.directwebremoting.servlet.DwrServlet
-
-
- debug
- true
-
-
-
- XFormsPostServlet
-
- de.betterform.agent.web.servlet.XFormsPostServlet
-
-
-
- FormsServlet
-
- de.betterform.agent.web.servlet.FormsServlet
-
-
-
- inspector
-
- de.betterform.agent.web.servlet.XFormsInspectorServlet
-
-
-
- ResourceServlet
-
- de.betterform.agent.web.resources.ResourceServlet
-
-
-
- error
- de.betterform.agent.web.servlet.ErrorServlet
-
-
-
-
- ====================== betterFORM servlets mapping ======================
-
-
-
- Flux
- /Flux/*
-
-
- XFormsPostServlet
- /XFormsPost
-
-
- XQueryServlet
- *.xql
-
-
- FormsServlet
- /forms/formslist
-
-
- inspector
- /inspector/*
-
-
- ResourceServlet
- /bfResources/*
-
- caching
- true
-
-
-
- error
- /error/*
-
-
-
-
- ====================== betterFORM filter ======================
-
-
-
- XFormsFilter
-
- de.betterform.agent.web.filter.XFormsFilter
-
-
-
-
-
- ====================== betterFORM filter mapping ======================
-
-
-
- XFormsFilter
- /apps/*
-
-
- XFormsFilter
- XFormsPostServlet
-
-
-
-
- de.betterform.agent.web.servlet.BfServletContextListener
-
-
-
- ====================== betterFORM filter and servlets ======================
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ====================== betterFORM configuration file ======================
+
+
+
+ betterform.configfile
+ WEB-INF/betterform-config.xml
+
+
+
+
+
+
+
+
+ ====================== betterFORM filter and servlets ======================
+
+
+
+ ====================== betterFORM servlets ======================
+
+
+
+ Flux
+
+ org.directwebremoting.servlet.DwrServlet
+
+
+ debug
+ true
+
+
+
+ XFormsPostServlet
+
+ de.betterform.agent.web.servlet.XFormsPostServlet
+
+
+
+ FormsServlet
+
+ de.betterform.agent.web.servlet.FormsServlet
+
+
+
+ inspector
+
+ de.betterform.agent.web.servlet.XFormsInspectorServlet
+
+
+
+ ResourceServlet
+
+ de.betterform.agent.web.resources.ResourceServlet
+
+
+
+ error
+ de.betterform.agent.web.servlet.ErrorServlet
+
+
+
+
+ ====================== betterFORM servlets mapping ======================
+
+
+
+ Flux
+ /Flux/*
+
+
+ XFormsPostServlet
+ /XFormsPost
+
+
+ XQueryServlet
+ *.xql
+
+
+ FormsServlet
+ /forms/formslist
+
+
+ inspector
+ /inspector/*
+
+
+ ResourceServlet
+ /bfResources/*
+
+ caching
+ true
+
+
+
+ error
+ /error/*
+
+
+
+
+ ====================== betterFORM filter ======================
+
+
+
+ XFormsFilter
+
+ de.betterform.agent.web.filter.XFormsFilter
+
+
+
+
+
+ ====================== betterFORM filter mapping ======================
+
+
+
+ XFormsFilter
+ /apps/*
+
+
+ XFormsFilter
+ XFormsPostServlet
+
+
+
+
+ de.betterform.agent.web.servlet.BfServletContextListener
+
+
+
+ ====================== betterFORM filter and servlets ======================
+
+
+
+
+
+
+
+
+
+
diff --git a/extensions/fluent/src/org/exist/fluent/AttributeBuilder.java b/extensions/fluent/src/org/exist/fluent/AttributeBuilder.java
index 9ccfb939045..5eb61451ff8 100644
--- a/extensions/fluent/src/org/exist/fluent/AttributeBuilder.java
+++ b/extensions/fluent/src/org/exist/fluent/AttributeBuilder.java
@@ -1,151 +1,151 @@
-package org.exist.fluent;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.log4j.Logger;
-import org.exist.dom.NodeListImpl;
-import org.w3c.dom.*;
-
-/**
- * Allows attributes to be added to, replaced in and removed from an existing
- * element in the database. The updates are batched for efficiency; you must call
- * {@link #commit()} to apply them to the database.
- *
- * @author Piotr Kaminski
- */
-public class AttributeBuilder {
-
- interface CompletedCallback {
- public void completed(NodeList removeList, NodeList addList);
- }
-
- private static final Logger LOG = Logger.getLogger(AttributeBuilder.class);
-
- private final CompletedCallback callback;
- private final NamespaceMap namespaceBindings;
- private final Element element;
- private boolean done;
- private final org.w3c.dom.Document doc;
- private final NodeListImpl removedAttributes = new NodeListImpl(), addedAttributes = new NodeListImpl();
- private final Map removedMap = new HashMap(), addedMap = new HashMap();
-
- AttributeBuilder(Element element, NamespaceMap namespaceBindings, CompletedCallback callback) {
- this.element = element;
- this.callback = callback;
- this.namespaceBindings = namespaceBindings.extend();
- this.doc = ElementBuilder.createDocumentNode();
- }
-
- private void checkDone() {
- if (done) throw new IllegalStateException("builder already done");
- }
-
- /**
- * Add a namespace binding to this builder's namespaces map.
- *
- * @param key the prefix to bind
- * @param uri the URI to bind it to
- * @return this attribute builder, for chaining calls
- */
- public AttributeBuilder namespace(String key, String uri) {
- namespaceBindings.put(key, uri);
- return this;
- }
-
- /**
- * Create a new attribute or change the value of an existing one. Later calls will overwrite
- * the values set by earlier ones.
- *
- * @param name the name of the attribute
- * @param value the value of the attribute, if not a String will be converted using {@link DataUtils#toXMLString(Object)}
- * @return this attribute builder, for chaining calls
- */
- public AttributeBuilder attr(String name, Object value) {
- checkDone();
- QName qname = QName.parse(name, namespaceBindings, null);
-
- // if previously added, this value will overwrite so remove old attribute from list
- Attr attr = addedMap.get(qname);
- if (attr != null) addedAttributes.remove(attr);
-
- // should be removed iff it is currently set on the element
- attr = qname.getAttributeNode(element);
- if (attr != null) {
- if (!removedMap.containsKey(qname)) {
- removedAttributes.add(attr);
- removedMap.put(qname, attr);
- }
- } else {
- assert !removedMap.containsKey(qname);
- }
-
- attr = qname.createAttribute(doc);
- attr.setValue(DataUtils.toXMLString(value));
- addedAttributes.add(attr);
- addedMap.put(qname, attr);
-
- return this;
- }
-
- /**
- * Add an attribute or change an existing attribute's value only if the given condition holds.
- * Behaves just like {@link #attr(String, Object)} if condition is true, does
- * nothing otherwise.
- *
- * @param condition the condition that must be satisfied before the attribute's value is set
- * @param name the name of the attribute
- * @param value the value of the attribute
- * @return this attribute builder, for chaining calls
- */
- public AttributeBuilder attrIf(boolean condition, String name, Object value) {
- checkDone();
- if (condition) attr(name, value);
- return this;
- }
-
- /**
- * Delete an attribute. Does nothing if the attribute does not exist. It's allowed (though probably
- * pointless) to delete attributes that were created using {@link #attr(String, Object)}, even if not
- * yet committed.
- *
- * @param name the name of the attribute to delete
- * @return this attribute builder, for chaining
- */
- public AttributeBuilder delAttr(String name) {
- checkDone();
- QName qname = QName.parse(name, namespaceBindings, null);
-
- // override any previous addition
- Attr attr = addedMap.get(qname);
- if (attr != null) {
- addedAttributes.remove(attr);
- addedMap.remove(qname);
- }
-
- // if currently set on the element, and not already listed for removal, remove it
- attr = qname.getAttributeNode(element);
- if (attr != null && !removedMap.containsKey(qname)) {
- removedAttributes.add(attr);
- removedMap.put(qname, attr);
- }
-
- return this;
- }
-
- /**
- * Commit the attribute changes recorded with the other methods to the database.
- */
- public void commit() {
- checkDone();
- done = true;
- if (removedAttributes.isEmpty() && addedAttributes.isEmpty()) return;
- callback.completed(removedAttributes, addedAttributes);
- }
-
- @Override
- protected void finalize() {
- if (!done) LOG.warn("disposed without commit");
- }
-
-}
+package org.exist.fluent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.exist.dom.NodeListImpl;
+import org.w3c.dom.*;
+
+/**
+ * Allows attributes to be added to, replaced in and removed from an existing
+ * element in the database. The updates are batched for efficiency; you must call
+ * {@link #commit()} to apply them to the database.
+ *
+ * @author Piotr Kaminski
+ */
+public class AttributeBuilder {
+
+ interface CompletedCallback {
+ public void completed(NodeList removeList, NodeList addList);
+ }
+
+ private static final Logger LOG = Logger.getLogger(AttributeBuilder.class);
+
+ private final CompletedCallback callback;
+ private final NamespaceMap namespaceBindings;
+ private final Element element;
+ private boolean done;
+ private final org.w3c.dom.Document doc;
+ private final NodeListImpl removedAttributes = new NodeListImpl(), addedAttributes = new NodeListImpl();
+ private final Map removedMap = new HashMap(), addedMap = new HashMap();
+
+ AttributeBuilder(Element element, NamespaceMap namespaceBindings, CompletedCallback callback) {
+ this.element = element;
+ this.callback = callback;
+ this.namespaceBindings = namespaceBindings.extend();
+ this.doc = ElementBuilder.createDocumentNode();
+ }
+
+ private void checkDone() {
+ if (done) throw new IllegalStateException("builder already done");
+ }
+
+ /**
+ * Add a namespace binding to this builder's namespaces map.
+ *
+ * @param key the prefix to bind
+ * @param uri the URI to bind it to
+ * @return this attribute builder, for chaining calls
+ */
+ public AttributeBuilder namespace(String key, String uri) {
+ namespaceBindings.put(key, uri);
+ return this;
+ }
+
+ /**
+ * Create a new attribute or change the value of an existing one. Later calls will overwrite
+ * the values set by earlier ones.
+ *
+ * @param name the name of the attribute
+ * @param value the value of the attribute, if not a String will be converted using {@link DataUtils#toXMLString(Object)}
+ * @return this attribute builder, for chaining calls
+ */
+ public AttributeBuilder attr(String name, Object value) {
+ checkDone();
+ QName qname = QName.parse(name, namespaceBindings, null);
+
+ // if previously added, this value will overwrite so remove old attribute from list
+ Attr attr = addedMap.get(qname);
+ if (attr != null) addedAttributes.remove(attr);
+
+ // should be removed iff it is currently set on the element
+ attr = qname.getAttributeNode(element);
+ if (attr != null) {
+ if (!removedMap.containsKey(qname)) {
+ removedAttributes.add(attr);
+ removedMap.put(qname, attr);
+ }
+ } else {
+ assert !removedMap.containsKey(qname);
+ }
+
+ attr = qname.createAttribute(doc);
+ attr.setValue(DataUtils.toXMLString(value));
+ addedAttributes.add(attr);
+ addedMap.put(qname, attr);
+
+ return this;
+ }
+
+ /**
+ * Add an attribute or change an existing attribute's value only if the given condition holds.
+ * Behaves just like {@link #attr(String, Object)} if condition is true, does
+ * nothing otherwise.
+ *
+ * @param condition the condition that must be satisfied before the attribute's value is set
+ * @param name the name of the attribute
+ * @param value the value of the attribute
+ * @return this attribute builder, for chaining calls
+ */
+ public AttributeBuilder attrIf(boolean condition, String name, Object value) {
+ checkDone();
+ if (condition) attr(name, value);
+ return this;
+ }
+
+ /**
+ * Delete an attribute. Does nothing if the attribute does not exist. It's allowed (though probably
+ * pointless) to delete attributes that were created using {@link #attr(String, Object)}, even if not
+ * yet committed.
+ *
+ * @param name the name of the attribute to delete
+ * @return this attribute builder, for chaining
+ */
+ public AttributeBuilder delAttr(String name) {
+ checkDone();
+ QName qname = QName.parse(name, namespaceBindings, null);
+
+ // override any previous addition
+ Attr attr = addedMap.get(qname);
+ if (attr != null) {
+ addedAttributes.remove(attr);
+ addedMap.remove(qname);
+ }
+
+ // if currently set on the element, and not already listed for removal, remove it
+ attr = qname.getAttributeNode(element);
+ if (attr != null && !removedMap.containsKey(qname)) {
+ removedAttributes.add(attr);
+ removedMap.put(qname, attr);
+ }
+
+ return this;
+ }
+
+ /**
+ * Commit the attribute changes recorded with the other methods to the database.
+ */
+ public void commit() {
+ checkDone();
+ done = true;
+ if (removedAttributes.isEmpty() && addedAttributes.isEmpty()) return;
+ callback.completed(removedAttributes, addedAttributes);
+ }
+
+ @Override
+ protected void finalize() {
+ if (!done) LOG.warn("disposed without commit");
+ }
+
+}
diff --git a/extensions/fluent/src/org/exist/fluent/DataUtils.java b/extensions/fluent/src/org/exist/fluent/DataUtils.java
index b91aa4a8d22..565c887f62a 100644
--- a/extensions/fluent/src/org/exist/fluent/DataUtils.java
+++ b/extensions/fluent/src/org/exist/fluent/DataUtils.java
@@ -1,119 +1,119 @@
-package org.exist.fluent;
-
-import java.util.*;
-
-import javax.xml.datatype.*;
-
-import org.exist.util.Base64Encoder;
-
-/**
- * A bunch of static data conversion utility methods.
- *
- * @author Piotr Kaminski
- */
-public class DataUtils {
- private DataUtils() {}
-
- /**
- * A comparator for dateTimes (XMLGregorianCalendar objects), that uses the partial order defined on dateTimes and throws an exception if the order is indeterminate.
- */
- public static final Comparator DATE_TIME_COMPARATOR = new Comparator() {
- public int compare(XMLGregorianCalendar a, XMLGregorianCalendar b) {
- int r = a.compare(b);
- if (r == DatatypeConstants.INDETERMINATE) throw new RuntimeException("date-times not comparable: " + a + " and " + b);
- return r;
- }
- };
-
- private static DatatypeFactory datatypeFactory;
- static {
- try {
- datatypeFactory = DatatypeFactory.newInstance();
- } catch (DatatypeConfigurationException e) {
- throw new RuntimeException("unable to configure datatype factory", e);
- }
- }
-
- /**
- * Return a shared instance of a datatype factory, used for creating new XML data objects.
- *
- * @return a shared datatype factory
- */
- public static DatatypeFactory datatypeFactory() {
- return datatypeFactory;
- }
-
- /**
- * Convert an XML date/time to its java.util.Date equivalent.
- *
- * @param dateTime the XML date/time to convert
- * @return a Java date
- */
- public static Date toDate(XMLGregorianCalendar dateTime) {
- return dateTime.toGregorianCalendar().getTime();
- }
-
- /**
- * Convert a Java date to its XML date/time equivalent.
- *
- * @param date the Java date to convert
- * @return an XML date/time
- */
- public static XMLGregorianCalendar toDateTime(Date date) {
- GregorianCalendar cal = new GregorianCalendar();
- cal.setTime(date);
- return datatypeFactory().newXMLGregorianCalendar(cal).normalize();
- }
-
- /**
- * Convert milliseconds offset to its XML date/time equivalent.
- *
- * @param millis a millisecond count since the epoch
- * @return an XML date/time
- */
- public static XMLGregorianCalendar toDateTime(long millis) {
- GregorianCalendar cal = new GregorianCalendar();
- cal.setTimeInMillis(millis);
- return datatypeFactory().newXMLGregorianCalendar(cal).normalize();
- }
-
- /**
- * Convert a Java object to its equivalent XML datatype string representation.
- * At the moment, there is special treatment for java.util.Date,
- * java.util.Calendar and byte[] (Base64 encoding);
- * for all other objects, we simply invoke toString().
- *
- * @param o the object to convert
- * @return a string representation of the object, according to XML Schema Datatype rules if possible
- */
- public static String toXMLString(Object o) {
- Object r = toXMLObject(o);
- if (r.getClass() != String.class) r = r.toString();
- return (String) r;
- }
-
- /**
- * Convert a Java object to its equivalent XML datatype representation.
- * At the moment, there is special treatment for java.util.Date,
- * java.util.Calendar and byte[] (Base64 encoding);
- * all other objects are simply passed through as-is.
- *
- * @param o the object to convert
- * @return an XML representation of the object, according to XML Schema Datatype rules if possible
- */
- public static Object toXMLObject(Object o) {
- if (o instanceof Date) {
- return toDateTime((Date) o).toString();
- } else if (o instanceof Calendar) {
- return toDateTime(((Calendar) o).getTimeInMillis()).toString();
- } else if (o instanceof byte[]) {
- Base64Encoder encoder = new Base64Encoder();
- encoder.translate((byte[]) o);
- return String.valueOf(encoder.getCharArray());
- } else {
- return o;
- }
- }
-
-
-}
+package org.exist.fluent;
+
+import java.util.*;
+
+import javax.xml.datatype.*;
+
+import org.exist.util.Base64Encoder;
+
+/**
+ * A bunch of static data conversion utility methods.
+ *
+ * @author Piotr Kaminski
+ */
+public class DataUtils {
+ private DataUtils() {}
+
+ /**
+ * A comparator for dateTimes (XMLGregorianCalendar objects), that uses the partial order defined on dateTimes and throws an exception if the order is indeterminate.
+ */
+ public static final Comparator DATE_TIME_COMPARATOR = new Comparator() {
+ public int compare(XMLGregorianCalendar a, XMLGregorianCalendar b) {
+ int r = a.compare(b);
+ if (r == DatatypeConstants.INDETERMINATE) throw new RuntimeException("date-times not comparable: " + a + " and " + b);
+ return r;
+ }
+ };
+
+ private static DatatypeFactory datatypeFactory;
+ static {
+ try {
+ datatypeFactory = DatatypeFactory.newInstance();
+ } catch (DatatypeConfigurationException e) {
+ throw new RuntimeException("unable to configure datatype factory", e);
+ }
+ }
+
+ /**
+ * Return a shared instance of a datatype factory, used for creating new XML data objects.
+ *
+ * @return a shared datatype factory
+ */
+ public static DatatypeFactory datatypeFactory() {
+ return datatypeFactory;
+ }
+
+ /**
+ * Convert an XML date/time to its java.util.Date equivalent.
+ *
+ * @param dateTime the XML date/time to convert
+ * @return a Java date
+ */
+ public static Date toDate(XMLGregorianCalendar dateTime) {
+ return dateTime.toGregorianCalendar().getTime();
+ }
+
+ /**
+ * Convert a Java date to its XML date/time equivalent.
+ *
+ * @param date the Java date to convert
+ * @return an XML date/time
+ */
+ public static XMLGregorianCalendar toDateTime(Date date) {
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTime(date);
+ return datatypeFactory().newXMLGregorianCalendar(cal).normalize();
+ }
+
+ /**
+ * Convert milliseconds offset to its XML date/time equivalent.
+ *
+ * @param millis a millisecond count since the epoch
+ * @return an XML date/time
+ */
+ public static XMLGregorianCalendar toDateTime(long millis) {
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTimeInMillis(millis);
+ return datatypeFactory().newXMLGregorianCalendar(cal).normalize();
+ }
+
+ /**
+ * Convert a Java object to its equivalent XML datatype string representation.
+ * At the moment, there is special treatment for java.util.Date,
+ * java.util.Calendar and byte[] (Base64 encoding);
+ * for all other objects, we simply invoke toString().
+ *
+ * @param o the object to convert
+ * @return a string representation of the object, according to XML Schema Datatype rules if possible
+ */
+ public static String toXMLString(Object o) {
+ Object r = toXMLObject(o);
+ if (r.getClass() != String.class) r = r.toString();
+ return (String) r;
+ }
+
+ /**
+ * Convert a Java object to its equivalent XML datatype representation.
+ * At the moment, there is special treatment for java.util.Date,
+ * java.util.Calendar and byte[] (Base64 encoding);
+ * all other objects are simply passed through as-is.
+ *
+ * @param o the object to convert
+ * @return an XML representation of the object, according to XML Schema Datatype rules if possible
+ */
+ public static Object toXMLObject(Object o) {
+ if (o instanceof Date) {
+ return toDateTime((Date) o).toString();
+ } else if (o instanceof Calendar) {
+ return toDateTime(((Calendar) o).getTimeInMillis()).toString();
+ } else if (o instanceof byte[]) {
+ Base64Encoder encoder = new Base64Encoder();
+ encoder.translate((byte[]) o);
+ return String.valueOf(encoder.getCharArray());
+ } else {
+ return o;
+ }
+ }
+
+
+}
diff --git a/extensions/fluent/src/org/exist/fluent/Database.java b/extensions/fluent/src/org/exist/fluent/Database.java
index b35b0253c42..fa817c2bcb3 100644
--- a/extensions/fluent/src/org/exist/fluent/Database.java
+++ b/extensions/fluent/src/org/exist/fluent/Database.java
@@ -1,656 +1,656 @@
-package org.exist.fluent;
-
-import java.io.File;
-import java.text.MessageFormat;
-import java.util.*;
-
-import org.apache.log4j.Logger;
-import org.exist.EXistException;
-import org.exist.backup.*;
-import org.exist.collections.*;
-import org.exist.collections.Collection;
-import org.exist.dom.*;
-import org.exist.security.*;
-import org.exist.security.xacml.AccessContext;
-import org.exist.storage.*;
-import org.exist.storage.lock.Lock;
-import org.exist.storage.sync.Sync;
-import org.exist.storage.txn.TransactionManager;
-import org.exist.util.*;
-import org.exist.xmldb.XmldbURI;
-import org.exist.xquery.*;
-import org.exist.xquery.value.*;
-
-/**
- *
The global entry point to an embedded instance of the eXist database.
- * The static methods on this class control the lifecycle of the database connection. It follows that
- * there can be only one embedded database running in the JVM (or rather one per classloader, but
- * that would probably be a bit confusing). To gain access to the contents of the database, you
- * need to acquire a handle instance by logging in. All operations performed based on that instance
- * will be executed using the permissions of the user associated with that instance. You can have
- * any number of instances (including multiple ones for the same user), but cannot mix resources
- * obtained from different instances. There is no need to explicitly release instances.
- *
- *
Here's a short example of how to start up the database, perform a query, and shut down:
- *
Database.startup(new File("conf.xml"));
- * Database db = Database.login("admin", "passwd");
- * for (String name : db.getFolder("/").query().all("//user/@name").values())
- * System.out.println("user: " + name);
- * Database.shutdown();
- *
- * @author Piotr Kaminski
- * @version $Revision: 1.26 $ ($Date: 2006/09/04 06:09:05 $)
- */
-public class Database {
-
- private static final Logger LOG = Logger.getLogger(Database.class);
-
- /**
- * Start up the database, configured using the given config file. This method must be
- * called precisely once before making use of any facilities offered in this package. The
- * configuration file is typically called 'conf.xml' and you can find a sample one in the root
- * directory of eXist's distribution.
- *
- * @param configFile the config file that specifies the database to use
- * @throws IllegalStateException if the database has already been started
- */
- public static void startup(File configFile) {
- try {
- if (isStarted()) throw new IllegalStateException("database already started");
- configFile = configFile.getAbsoluteFile();
- Configuration config = new Configuration(configFile.getName(), configFile.getParentFile().getAbsolutePath());
- BrokerPool.configure(dbName, 1, 5, config);
- pool = BrokerPool.getInstance(dbName);
- txManager = pool.getTransactionManager();
- configureRootCollection(configFile);
- defragmenter.start();
- QueryService.statistics().reset();
- } catch (DatabaseConfigurationException e) {
- throw new DatabaseException(e);
- } catch (EXistException e) {
- throw new DatabaseException(e);
- }
- }
-
- static void configureRootCollection(File configFile) {
- Database db = new Database(pool.getSecurityManager().getSystemSubject());
- StringBuilder configXml = new StringBuilder();
- configXml.append("");
- configXml.append(ListenerManager.getTriggerConfigXml());
- {
- XMLDocument configDoc = db.getFolder("/").documents().load(Name.generate(db), Source.xml(configFile));
- Node indexNode = configDoc.query().optional("/exist/indexer/index").node();
- if (indexNode.extant()) configXml.append(indexNode.toString());
- configDoc.delete();
- }
- configXml.append("");
-
- // If the config is already *exactly* how we want it, no need to reload and reindex.
- try {
- Node currentConfig =
- db.getFolder(XmldbURI.CONFIG_COLLECTION + Database.ROOT_PREFIX).documents()
- .get(CollectionConfiguration.DEFAULT_COLLECTION_CONFIG_FILE).xml().root();
- if (currentConfig.query().presub().single("deep-equal(., $1)", configXml.toString()).booleanValue()) return;
- } catch (DatabaseException e) {
- // fall through
- }
-
- // Now force reload and reindex so it'll pick up the new settings.
- Transaction tx = db.requireTransactionWithBroker();
- try {
- pool.getConfigurationManager().addConfiguration(tx.tx, tx.broker, tx.broker.getCollection(XmldbURI.ROOT_COLLECTION_URI), configXml.toString());
- tx.commit();
- DBBroker broker = db.acquireBroker();
- try {
- broker.reindexCollection(XmldbURI.ROOT_COLLECTION_URI);
- } finally {
- db.releaseBroker(broker);
- }
- } catch (PermissionDeniedException e) {
- throw new DatabaseException(e);
- } catch (CollectionConfigurationException e) {
- throw new DatabaseException(e);
- } finally {
- tx.abortIfIncomplete();
- }
- }
-
- /**
- * Shut down the database connection. If the database is not started, do nothing.
- */
- public static void shutdown() {
- if (pool == null) return;
- defragmenter.stop();
- pool.shutdown();
- pool = null;
- }
-
- /**
- * Ensure the database is started. If the database is not started, start it with the
- * given config file. If it is already started, make sure it was started with the same
- * config file.
- *
- * @param configFile the config file that specifies the database to use
- * @throws IllegalStateException if the database was already started with a different config file
- *
- * @deprecated Please use a combination of {@link #isStarted()} and {@link #startup(File)}.
- */
- @Deprecated public static void ensureStarted(File configFile) {
- if (isStarted()) {
- String currentPath = pool.getConfiguration().getConfigFilePath();
- if (!configFile.getAbsoluteFile().equals(new File(currentPath).getAbsoluteFile()))
- throw new IllegalStateException("database already started with different configuration " + currentPath);
- } else {
- startup(configFile);
- }
- }
-
- /**
- * Return whether the database has been started and is currently running in this JVM. This will
- * be the case if {@link #startup(File)} or {@link #ensureStarted(File)} was previously called
- * successfully and {@link #shutdown()} was not yet called.
- *
- * @return true if the database has been started with any configuration file
- */
- public static boolean isStarted() {
- return BrokerPool.isConfigured(dbName);
- }
-
- /**
- * Flush the contents of the database to disk. This ensures that all transactions are written out
- * and the state of the database is synced. It shouldn't be necessary any more with the newly
- * implemented transaction recovery and this method will probably be deprecated in the future.
- */
- public static void flush() {
- if (!BrokerPool.isConfigured(dbName)) throw new IllegalStateException("database not started");
- try {
- DBBroker broker = pool.get(pool.getSecurityManager().getSystemSubject());
- try {
- broker.flush();
- broker.sync(Sync.MAJOR_SYNC);
- } finally {
- pool.release(broker);
- }
- } catch (EXistException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * Verify the internal consistency of the database's data structures. Log a fatal message if the
- * database is corrupted, as well as error-level messages for all the problems found. If the
- * database is corrupted, you can try using admin tools to reindex it, or back it up and restore
- * it. However, there's a good chance it's unrecoverable.
- *
- * @return true if the database's internal data structures are consistent,
- * false if the database is corrupted
- */
- public static boolean checkConsistency() {
- synchronized(pool) {
- try {
- DBBroker broker = pool.enterServiceMode(pool.getSecurityManager().getSystemSubject());
- try {
- List errors = new ConsistencyCheck(broker, false).checkAll(NULL_PROGRESS_CALLBACK);
- if (errors.isEmpty()) return true;
- LOG.fatal("database corrupted");
- for (ErrorReport error : errors) LOG.error(error.toString().replace("\n", " "));
- return false;
- } finally {
- pool.exitServiceMode(pool.getSecurityManager().getSystemSubject());
- }
- } catch (TerminatedException e) {
- throw new DatabaseException(e);
- } catch (PermissionDeniedException e) {
- throw new DatabaseException(e);
- }
- }
- }
-
- private static final ConsistencyCheck.ProgressCallback NULL_PROGRESS_CALLBACK = new ConsistencyCheck.ProgressCallback() {
- public void error(ErrorReport error) {}
- public void startCollection(String path) {}
- public void startDocument(String name, int current, int count) {}
- };
-
- /**
- * Login to obtain access to the database. The password should be passed in the clear.
- * If a user does not have a password set, pass in null.
- * Note that all newly created databases have a user admin with no password set.
- *
- * @param username the username of the user being logged in
- * @param password the password corresponding to that user name, or null if none
- * @return an instance of the database configured for access by the given user
- * @throws DatabaseException if the user could not be logged in
- */
- public static Database login(String username, String password) {
- Subject user;
- try {
- user = pool.getSecurityManager().authenticate(username, password);
- } catch (AuthenticationException e) {
- throw new DatabaseException(e.getMessage(),e);
- }
- return new Database(user);
- }
-
- /**
- * Get database if was already login in current thread.
- *
- * @return an instance of the database configured for access
- * @throws DatabaseException if the user could not be logged in
- */
- public static Database current() throws DatabaseException {
- if (pool == null) {
- //if (isStarted()) throw new IllegalStateException("database already started");
- try {
- pool = BrokerPool.getInstance(dbName);
- txManager = pool.getTransactionManager();
- //configureRootCollection(configFile);
- //defragmenter.start();
- //QueryService.statistics().reset();
- } catch (EXistException e) {
- throw new DatabaseException(e);
- }
- }
- DBBroker broker;
- try {
- broker = pool.getActiveBroker();
- } catch (Throwable e) {
- throw new DatabaseException(e);
- }
-
- return new Database( broker.getSubject() );
- }
-
- /**
- * Remove the given listener from all trigger points on all sources.
- *
- * @param listener the listener to remove
- */
- public static void remove(Listener listener) {
- ListenerManager.INSTANCE.remove(listener);
- }
-
- static String normalizePath(String path) {
- if (path.startsWith(ROOT_PREFIX)) {
- path = path.equals(ROOT_PREFIX) ? "/" : path.substring(Database.ROOT_PREFIX.length());
- }
- return path;
- }
-
- private static String dbName = "exist";
- public static final String ROOT_PREFIX = XmldbURI.ROOT_COLLECTION;
- private static volatile BrokerPool pool;
- private static TransactionManager txManager;
- private static final ThreadLocal localTransaction = new ThreadLocal();
- private static final WeakHashMap instrumentedBrokers = new WeakHashMap();
-
- private final Subject user;
- private final NamespaceMap namespaceBindings;
- String defaultCharacterEncoding = "UTF-8";
-
- Database(Subject user) {
- this.user = user;
- this.namespaceBindings = new NamespaceMap();
- }
-
- Database(Database parent, NamespaceMap namespaceBindings) {
- this.user = parent.user;
- this.namespaceBindings = namespaceBindings.extend();
- }
-
- /**
- * @deprecated Renamed to {@link #setDefaultCharacterEncoding(String)}.
- */
- @Deprecated public void setDefaultExportEncoding(String encoding) {
- setDefaultCharacterEncoding(encoding);
- }
-
- /**
- * Set the default character encoding to be used when exporting XML files from the database.
- * If not explicitly set, it defaults to UTF-8.
- *
- * @param encoding
- */
- public void setDefaultCharacterEncoding(String encoding) {
- defaultCharacterEncoding = encoding;
- }
-
- DBBroker acquireBroker() {
- try {
- NativeBroker broker = (NativeBroker) pool.get(user);
- if (instrumentedBrokers.get(broker) == null) {
- broker.addContentLoadingObserver(contentObserver);
- instrumentedBrokers.put(broker, Boolean.TRUE);
- }
- return broker;
- } catch (EXistException e) {
- throw new DatabaseException(e);
- }
- }
-
- void releaseBroker(DBBroker broker) {
- pool.release(broker);
- }
-
- /**
- * Return the namespace bindings for this database instance. They will be inherited by
- * all resources derived from this instance.
- *
- * @return the namespace bindings for this database instance
- */
- public NamespaceMap namespaceBindings() {
- return namespaceBindings;
- }
-
- private Sequence adoptInternal(Object o) {
- DBBroker broker = acquireBroker();
- try {
- XQueryContext context = broker.getXQueryService().newContext(AccessContext.INTERNAL_PREFIX_LOOKUP);
- context.declareNamespaces(namespaceBindings.getCombinedMap());
- context.setBackwardsCompatibility(false);
- context.setStaticallyKnownDocuments(DocumentSet.EMPTY_DOCUMENT_SET);
- return XPathUtil.javaObjectToXPath(o, context, true);
- } catch (XPathException e) {
- throw new DatabaseException(e);
- } finally {
- releaseBroker(broker);
- }
- }
-
- public ItemList adopt(org.w3c.dom.Node node) {
- // this works for DocumentFragments too, they'll be automatically expanded
- return new ItemList(adoptInternal(node), namespaceBindings.extend(), this);
- }
-
- /**
- * Check whether the database contains a document or a folder with the given absolute path.
- *
- * @param path the absolute path of the document or folder to check
- * @return true if there is a document or folder at the given path, false otherwise
- */
- public boolean contains(String path) {
- if (path.length() == 0) throw new IllegalArgumentException("empty path: " + path);
- if (path.equals("/")) return true;
- if (!path.startsWith("/")) throw new IllegalArgumentException("path not absolute: " + path);
- if (path.endsWith("/")) throw new IllegalArgumentException("path ends with '/': " + path);
- int i = path.lastIndexOf('/');
- assert i != -1;
-
- DBBroker broker = acquireBroker();
- try {
- if (broker.getCollection(XmldbURI.create(path)) != null) return true;
- String folderPath = path.substring(0, i);
- String name = path.substring(i+1);
- Collection collection = broker.openCollection(XmldbURI.create(folderPath), Lock.NO_LOCK);
- if (collection == null) return false;
- return collection.getDocument(broker, XmldbURI.create(name)) != null;
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- releaseBroker(broker);
- }
- }
-
- /**
- * Get the document for the given absolute path. Namespace bindings will be inherited
- * from this database.
- *
- * @param path the absolute path of the desired document
- * @return the document at the given path
- * @throws DatabaseException if the document is not found or something else goes wrong
- */
- public Document getDocument(String path) {
- if (path.length() == 0) throw new IllegalArgumentException("empty document path: " + path);
- if (!path.startsWith("/")) throw new IllegalArgumentException("document path not absolute: " + path);
- if (path.endsWith("/")) throw new IllegalArgumentException("document path ends with '/': " + path);
- int i = path.lastIndexOf('/');
- assert i != -1;
- return getFolder(i == 0 ? "/" : path.substring(0, i)).documents().get(path.substring(i+1));
- }
-
- /**
- * Get the folder for the given path. Namespace mappings will be inherited from this
- * database.
- *
- * @param path the address of the desired collection
- * @return a collection bound to the given path
- * @throws DatabaseException if the path does not identify a valid collection
- */
- public Folder getFolder(String path) {
- return new Folder(path, false, namespaceBindings.extend(), this);
- }
-
- /**
- * Create the folder for the given path. Namespace mappings will be inherited from this
- * database. If the folder does not exist, it is created along with all required ancestors.
- *
- * @param path the address of the desired collection
- * @return a collection bound to the given path
- */
- public Folder createFolder(String path) {
- return new Folder(path, true, namespaceBindings.extend(), this);
- }
-
- /**
- * Return a query service that runs queries over the given list of resources.
- * The resources can be of different kinds, and come from different locations in the
- * folder hierarchy. The service will inherit the database's namespace bindings,
- * rather than the bindings of any given context resource.
- *
- * @param context the arbitrary collection of database objects over which to query
- * @return a query service over the given resources
- */
- public QueryService query(Resource... context) {
- return query(Arrays.asList(context));
- }
-
- /**
- * Return a query service that runs queries over the given list of resources.
- * The resources can be of different kinds, and come from different locations in the
- * folder hierarchy. The service will inherit the database's namespace bindings,
- * rather than the bindings of any given context resource.
- *
- * @param context the arbitrary collection of database objects over which to query;
- * the collection is not copied, and the collection's contents are re-read every time the query is performed
- * @return a query service over the given resources
- */
- public QueryService query(final java.util.Collection extends Resource> context) {
- return new QueryService(getFolder("/")) {
- @Override void prepareContext(DBBroker broker) {
- MutableDocumentSet mdocs = new DefaultDocumentSet();
- base = new ValueSequence();
- for (Resource res : context) {
- QueryService qs = res.query();
- if (qs.docs != null) mdocs.addAll(qs.docs);
- if (qs.base != null) try {
- base.addAll(qs.base);
- } catch (XPathException e) {
- throw new DatabaseException("unexpected item type conflict", e);
- }
- }
- docs = mdocs;
- }
- };
- }
-
- /**
- * Return a transaction for use with database operations. If a transaction is already in progress
- * then join it, otherwise begin a new one. If a transaction is joined, calling commit
- * or abort on the returned instance will have no effect; only the outermost
- * transaction object can do this.
- *
- * @return a transaction object
- */
- static Transaction requireTransaction() {
- Transaction t = localTransaction.get();
- return t == null ? new Transaction(txManager, null) : new Transaction(t, null);
- }
-
- Transaction requireTransactionWithBroker() {
- Transaction t = localTransaction.get();
- return t == null ? new Transaction(txManager, this) : new Transaction(t, this);
- }
-
- void checkSame(Resource o) {
- // allow other resource to be a NULL, as those are safe and database-neutral
- if (!(o.database() == null || o.database().user == this.user)) throw new IllegalArgumentException("cannot combine objects from two database instances in one operation");
- }
-
- private static final WeakMultiValueHashMap staleMap = new WeakMultiValueHashMap();
-
- private static void stale(String key) {
- int updated = 0;
- synchronized(staleMap) {
- for (StaleMarker value : staleMap.get(key)) {value.mark(); updated++;}
- staleMap.remove(key);
- }
- }
-
- static void trackStale(String key, StaleMarker value) {
- staleMap.put(normalizePath(key), value);
- }
-
- private static final ContentLoadingObserver contentObserver = new ContentLoadingObserver() {
- public void dropIndex(Collection collection) {
- stale(normalizePath(collection.getURI().getCollectionPath()));
- }
- public void dropIndex(DocumentImpl doc) throws ReadOnlyException {
- stale(normalizePath(doc.getURI().getCollectionPath()));
- }
- public void removeNode(StoredNode node, NodePath currentPath, String content) {
- stale(normalizePath(((DocumentImpl) node.getOwnerDocument()).getURI().getCollectionPath()) + "#" + node.getNodeId());
- }
- public void flush() {}
- public void setDocument(DocumentImpl document) {}
- public void storeAttribute(AttrImpl node, NodePath currentPath, int indexingHint, RangeIndexSpec spec, boolean remove) {}
- public void storeText(TextImpl node, NodePath currentPath, int indexingHint) {}
- public void sync() {}
- public void printStatistics() {}
-
- public boolean close() {return true;}
- public void remove() {}
- public void closeAndRemove() {
- // TODO: do nothing OK here? indexes just got wiped and recreated, and this listener
- // was removed...
- }
-
- };
-
- static void queueDefrag(DocumentImpl doc) {
- defragmenter.queue(doc);
- }
-
- private static final Defragmenter defragmenter = new Defragmenter();
-
- private static class Defragmenter implements Runnable {
- private static final Logger LOG = Logger.getLogger("org.exist.fluent.Database.defragmenter");
- private static final long DEFRAG_INTERVAL = 10000; // ms
- private Set docsToDefrag = new TreeSet();
- private Thread thread;
-
- public void start() {
- if (thread != null) return;
- thread = new Thread(this, "Database defragmenter");
- thread.setPriority(Thread.NORM_PRIORITY-3);
- thread.setDaemon(true);
- thread.start();
- }
-
- public void stop() {
- if (thread == null) return;
- thread.interrupt();
- try {
- thread.join();
- } catch (InterruptedException e) {
- // oh well
- }
- thread = null;
- }
-
- public synchronized void queue(DocumentImpl doc) {
- docsToDefrag.add(doc);
- }
-
- public void run() {
- while(true) {
- try {
- Thread.sleep(DEFRAG_INTERVAL);
- } catch (InterruptedException e) {
- break;
- }
-
- // Grab copy of docsToDefrag to avoid potential deadlocks (if an executing query has a lock on
- // the document we want to defrag, we block, then if it tries to queue another document for
- // defrag it blocks, and it's deadlock time).
- Set docsToDefragCopy;
- synchronized(this) {
- LOG.debug(new MessageFormat(
- "checking for documents to defragment, {0,choice,0#no candidates|1#1 candidate|1<{0,number,integer} candidates}")
- .format(new Object[] {docsToDefrag.size()}));
- docsToDefragCopy = docsToDefrag;
- docsToDefrag = new TreeSet();
- }
-
- int count = 0;
- try {
- DBBroker broker = pool.get(pool.getSecurityManager().getSystemSubject());
- try {
- Integer fragmentationLimitObject = broker.getBrokerPool().getConfiguration().getInteger(DBBroker.PROPERTY_XUPDATE_FRAGMENTATION_FACTOR);
- int fragmentationLimit = fragmentationLimitObject == null ? 0 : fragmentationLimitObject;
- for (Iterator it = docsToDefragCopy.iterator(); it.hasNext(); ) {
- DocumentImpl doc = it.next();
- if (doc.getMetadata().getSplitCount() <= fragmentationLimit) {
- it.remove();
- } else {
- // Must hold write lock on doc before checking stale map to avoid race condition
- if (doc.getUpdateLock().attempt(Lock.WRITE_LOCK)) try {
- String docPath = normalizePath(doc.getURI().getCollectionPath());
- if (!staleMap.containsKey(docPath)) {
- LOG.debug("defragmenting " + docPath);
- count++;
- Transaction tx = Database.requireTransaction();
- try {
- broker.defragXMLResource(tx.tx, doc);
- tx.commit();
- it.remove();
- } finally {
- tx.abortIfIncomplete();
- }
- }
- } finally {
- doc.getUpdateLock().release(Lock.WRITE_LOCK);
- }
- }
- }
- } finally {
- pool.release(broker);
- }
- } catch (EXistException e) {
- LOG.error("unable to get broker with system privileges to defragment documents", e);
- }
-
- LOG.debug(new MessageFormat(
- "defragmented {0,choice,0#0 documents|1#1 document|1<{0,number,integer} documents}, next cycle in {1,number,integer}s")
- .format(new Object[] {count, DEFRAG_INTERVAL / 1000}));
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- static Iterator emptyIterator() {
- return EMPTY_ITERATOR;
- }
-
- @SuppressWarnings("unchecked")
- static final Iterator EMPTY_ITERATOR = new Iterator() {
- public boolean hasNext() {return false;}
- public Object next() {throw new NoSuchElementException();}
- public void remove() {throw new UnsupportedOperationException();}
- };
-
- @SuppressWarnings("unchecked")
- static final Iterable EMPTY_ITERABLE = new Iterable() {
- public Iterator iterator() {return EMPTY_ITERATOR;}
- };
-
-}
+package org.exist.fluent;
+
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.*;
+
+import org.apache.log4j.Logger;
+import org.exist.EXistException;
+import org.exist.backup.*;
+import org.exist.collections.*;
+import org.exist.collections.Collection;
+import org.exist.dom.*;
+import org.exist.security.*;
+import org.exist.security.xacml.AccessContext;
+import org.exist.storage.*;
+import org.exist.storage.lock.Lock;
+import org.exist.storage.sync.Sync;
+import org.exist.storage.txn.TransactionManager;
+import org.exist.util.*;
+import org.exist.xmldb.XmldbURI;
+import org.exist.xquery.*;
+import org.exist.xquery.value.*;
+
+/**
+ *
The global entry point to an embedded instance of the eXist database.
+ * The static methods on this class control the lifecycle of the database connection. It follows that
+ * there can be only one embedded database running in the JVM (or rather one per classloader, but
+ * that would probably be a bit confusing). To gain access to the contents of the database, you
+ * need to acquire a handle instance by logging in. All operations performed based on that instance
+ * will be executed using the permissions of the user associated with that instance. You can have
+ * any number of instances (including multiple ones for the same user), but cannot mix resources
+ * obtained from different instances. There is no need to explicitly release instances.
+ *
+ *
Here's a short example of how to start up the database, perform a query, and shut down:
+ *
Database.startup(new File("conf.xml"));
+ * Database db = Database.login("admin", "passwd");
+ * for (String name : db.getFolder("/").query().all("//user/@name").values())
+ * System.out.println("user: " + name);
+ * Database.shutdown();
+ *
+ * @author Piotr Kaminski
+ * @version $Revision: 1.26 $ ($Date: 2006/09/04 06:09:05 $)
+ */
+public class Database {
+
+ private static final Logger LOG = Logger.getLogger(Database.class);
+
+ /**
+ * Start up the database, configured using the given config file. This method must be
+ * called precisely once before making use of any facilities offered in this package. The
+ * configuration file is typically called 'conf.xml' and you can find a sample one in the root
+ * directory of eXist's distribution.
+ *
+ * @param configFile the config file that specifies the database to use
+ * @throws IllegalStateException if the database has already been started
+ */
+ public static void startup(File configFile) {
+ try {
+ if (isStarted()) throw new IllegalStateException("database already started");
+ configFile = configFile.getAbsoluteFile();
+ Configuration config = new Configuration(configFile.getName(), configFile.getParentFile().getAbsolutePath());
+ BrokerPool.configure(dbName, 1, 5, config);
+ pool = BrokerPool.getInstance(dbName);
+ txManager = pool.getTransactionManager();
+ configureRootCollection(configFile);
+ defragmenter.start();
+ QueryService.statistics().reset();
+ } catch (DatabaseConfigurationException e) {
+ throw new DatabaseException(e);
+ } catch (EXistException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ static void configureRootCollection(File configFile) {
+ Database db = new Database(pool.getSecurityManager().getSystemSubject());
+ StringBuilder configXml = new StringBuilder();
+ configXml.append("");
+ configXml.append(ListenerManager.getTriggerConfigXml());
+ {
+ XMLDocument configDoc = db.getFolder("/").documents().load(Name.generate(db), Source.xml(configFile));
+ Node indexNode = configDoc.query().optional("/exist/indexer/index").node();
+ if (indexNode.extant()) configXml.append(indexNode.toString());
+ configDoc.delete();
+ }
+ configXml.append("");
+
+ // If the config is already *exactly* how we want it, no need to reload and reindex.
+ try {
+ Node currentConfig =
+ db.getFolder(XmldbURI.CONFIG_COLLECTION + Database.ROOT_PREFIX).documents()
+ .get(CollectionConfiguration.DEFAULT_COLLECTION_CONFIG_FILE).xml().root();
+ if (currentConfig.query().presub().single("deep-equal(., $1)", configXml.toString()).booleanValue()) return;
+ } catch (DatabaseException e) {
+ // fall through
+ }
+
+ // Now force reload and reindex so it'll pick up the new settings.
+ Transaction tx = db.requireTransactionWithBroker();
+ try {
+ pool.getConfigurationManager().addConfiguration(tx.tx, tx.broker, tx.broker.getCollection(XmldbURI.ROOT_COLLECTION_URI), configXml.toString());
+ tx.commit();
+ DBBroker broker = db.acquireBroker();
+ try {
+ broker.reindexCollection(XmldbURI.ROOT_COLLECTION_URI);
+ } finally {
+ db.releaseBroker(broker);
+ }
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException(e);
+ } catch (CollectionConfigurationException e) {
+ throw new DatabaseException(e);
+ } finally {
+ tx.abortIfIncomplete();
+ }
+ }
+
+ /**
+ * Shut down the database connection. If the database is not started, do nothing.
+ */
+ public static void shutdown() {
+ if (pool == null) return;
+ defragmenter.stop();
+ pool.shutdown();
+ pool = null;
+ }
+
+ /**
+ * Ensure the database is started. If the database is not started, start it with the
+ * given config file. If it is already started, make sure it was started with the same
+ * config file.
+ *
+ * @param configFile the config file that specifies the database to use
+ * @throws IllegalStateException if the database was already started with a different config file
+ *
+ * @deprecated Please use a combination of {@link #isStarted()} and {@link #startup(File)}.
+ */
+ @Deprecated public static void ensureStarted(File configFile) {
+ if (isStarted()) {
+ String currentPath = pool.getConfiguration().getConfigFilePath();
+ if (!configFile.getAbsoluteFile().equals(new File(currentPath).getAbsoluteFile()))
+ throw new IllegalStateException("database already started with different configuration " + currentPath);
+ } else {
+ startup(configFile);
+ }
+ }
+
+ /**
+ * Return whether the database has been started and is currently running in this JVM. This will
+ * be the case if {@link #startup(File)} or {@link #ensureStarted(File)} was previously called
+ * successfully and {@link #shutdown()} was not yet called.
+ *
+ * @return true if the database has been started with any configuration file
+ */
+ public static boolean isStarted() {
+ return BrokerPool.isConfigured(dbName);
+ }
+
+ /**
+ * Flush the contents of the database to disk. This ensures that all transactions are written out
+ * and the state of the database is synced. It shouldn't be necessary any more with the newly
+ * implemented transaction recovery and this method will probably be deprecated in the future.
+ */
+ public static void flush() {
+ if (!BrokerPool.isConfigured(dbName)) throw new IllegalStateException("database not started");
+ try {
+ DBBroker broker = pool.get(pool.getSecurityManager().getSystemSubject());
+ try {
+ broker.flush();
+ broker.sync(Sync.MAJOR_SYNC);
+ } finally {
+ pool.release(broker);
+ }
+ } catch (EXistException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ /**
+ * Verify the internal consistency of the database's data structures. Log a fatal message if the
+ * database is corrupted, as well as error-level messages for all the problems found. If the
+ * database is corrupted, you can try using admin tools to reindex it, or back it up and restore
+ * it. However, there's a good chance it's unrecoverable.
+ *
+ * @return true if the database's internal data structures are consistent,
+ * false if the database is corrupted
+ */
+ public static boolean checkConsistency() {
+ synchronized(pool) {
+ try {
+ DBBroker broker = pool.enterServiceMode(pool.getSecurityManager().getSystemSubject());
+ try {
+ List errors = new ConsistencyCheck(broker, false).checkAll(NULL_PROGRESS_CALLBACK);
+ if (errors.isEmpty()) return true;
+ LOG.fatal("database corrupted");
+ for (ErrorReport error : errors) LOG.error(error.toString().replace("\n", " "));
+ return false;
+ } finally {
+ pool.exitServiceMode(pool.getSecurityManager().getSystemSubject());
+ }
+ } catch (TerminatedException e) {
+ throw new DatabaseException(e);
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException(e);
+ }
+ }
+ }
+
+ private static final ConsistencyCheck.ProgressCallback NULL_PROGRESS_CALLBACK = new ConsistencyCheck.ProgressCallback() {
+ public void error(ErrorReport error) {}
+ public void startCollection(String path) {}
+ public void startDocument(String name, int current, int count) {}
+ };
+
+ /**
+ * Login to obtain access to the database. The password should be passed in the clear.
+ * If a user does not have a password set, pass in null.
+ * Note that all newly created databases have a user admin with no password set.
+ *
+ * @param username the username of the user being logged in
+ * @param password the password corresponding to that user name, or null if none
+ * @return an instance of the database configured for access by the given user
+ * @throws DatabaseException if the user could not be logged in
+ */
+ public static Database login(String username, String password) {
+ Subject user;
+ try {
+ user = pool.getSecurityManager().authenticate(username, password);
+ } catch (AuthenticationException e) {
+ throw new DatabaseException(e.getMessage(),e);
+ }
+ return new Database(user);
+ }
+
+ /**
+ * Get database if was already login in current thread.
+ *
+ * @return an instance of the database configured for access
+ * @throws DatabaseException if the user could not be logged in
+ */
+ public static Database current() throws DatabaseException {
+ if (pool == null) {
+ //if (isStarted()) throw new IllegalStateException("database already started");
+ try {
+ pool = BrokerPool.getInstance(dbName);
+ txManager = pool.getTransactionManager();
+ //configureRootCollection(configFile);
+ //defragmenter.start();
+ //QueryService.statistics().reset();
+ } catch (EXistException e) {
+ throw new DatabaseException(e);
+ }
+ }
+ DBBroker broker;
+ try {
+ broker = pool.getActiveBroker();
+ } catch (Throwable e) {
+ throw new DatabaseException(e);
+ }
+
+ return new Database( broker.getSubject() );
+ }
+
+ /**
+ * Remove the given listener from all trigger points on all sources.
+ *
+ * @param listener the listener to remove
+ */
+ public static void remove(Listener listener) {
+ ListenerManager.INSTANCE.remove(listener);
+ }
+
+ static String normalizePath(String path) {
+ if (path.startsWith(ROOT_PREFIX)) {
+ path = path.equals(ROOT_PREFIX) ? "/" : path.substring(Database.ROOT_PREFIX.length());
+ }
+ return path;
+ }
+
+ private static String dbName = "exist";
+ public static final String ROOT_PREFIX = XmldbURI.ROOT_COLLECTION;
+ private static volatile BrokerPool pool;
+ private static TransactionManager txManager;
+ private static final ThreadLocal localTransaction = new ThreadLocal();
+ private static final WeakHashMap instrumentedBrokers = new WeakHashMap();
+
+ private final Subject user;
+ private final NamespaceMap namespaceBindings;
+ String defaultCharacterEncoding = "UTF-8";
+
+ Database(Subject user) {
+ this.user = user;
+ this.namespaceBindings = new NamespaceMap();
+ }
+
+ Database(Database parent, NamespaceMap namespaceBindings) {
+ this.user = parent.user;
+ this.namespaceBindings = namespaceBindings.extend();
+ }
+
+ /**
+ * @deprecated Renamed to {@link #setDefaultCharacterEncoding(String)}.
+ */
+ @Deprecated public void setDefaultExportEncoding(String encoding) {
+ setDefaultCharacterEncoding(encoding);
+ }
+
+ /**
+ * Set the default character encoding to be used when exporting XML files from the database.
+ * If not explicitly set, it defaults to UTF-8.
+ *
+ * @param encoding
+ */
+ public void setDefaultCharacterEncoding(String encoding) {
+ defaultCharacterEncoding = encoding;
+ }
+
+ DBBroker acquireBroker() {
+ try {
+ NativeBroker broker = (NativeBroker) pool.get(user);
+ if (instrumentedBrokers.get(broker) == null) {
+ broker.addContentLoadingObserver(contentObserver);
+ instrumentedBrokers.put(broker, Boolean.TRUE);
+ }
+ return broker;
+ } catch (EXistException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ void releaseBroker(DBBroker broker) {
+ pool.release(broker);
+ }
+
+ /**
+ * Return the namespace bindings for this database instance. They will be inherited by
+ * all resources derived from this instance.
+ *
+ * @return the namespace bindings for this database instance
+ */
+ public NamespaceMap namespaceBindings() {
+ return namespaceBindings;
+ }
+
+ private Sequence adoptInternal(Object o) {
+ DBBroker broker = acquireBroker();
+ try {
+ XQueryContext context = broker.getXQueryService().newContext(AccessContext.INTERNAL_PREFIX_LOOKUP);
+ context.declareNamespaces(namespaceBindings.getCombinedMap());
+ context.setBackwardsCompatibility(false);
+ context.setStaticallyKnownDocuments(DocumentSet.EMPTY_DOCUMENT_SET);
+ return XPathUtil.javaObjectToXPath(o, context, true);
+ } catch (XPathException e) {
+ throw new DatabaseException(e);
+ } finally {
+ releaseBroker(broker);
+ }
+ }
+
+ public ItemList adopt(org.w3c.dom.Node node) {
+ // this works for DocumentFragments too, they'll be automatically expanded
+ return new ItemList(adoptInternal(node), namespaceBindings.extend(), this);
+ }
+
+ /**
+ * Check whether the database contains a document or a folder with the given absolute path.
+ *
+ * @param path the absolute path of the document or folder to check
+ * @return true if there is a document or folder at the given path, false otherwise
+ */
+ public boolean contains(String path) {
+ if (path.length() == 0) throw new IllegalArgumentException("empty path: " + path);
+ if (path.equals("/")) return true;
+ if (!path.startsWith("/")) throw new IllegalArgumentException("path not absolute: " + path);
+ if (path.endsWith("/")) throw new IllegalArgumentException("path ends with '/': " + path);
+ int i = path.lastIndexOf('/');
+ assert i != -1;
+
+ DBBroker broker = acquireBroker();
+ try {
+ if (broker.getCollection(XmldbURI.create(path)) != null) return true;
+ String folderPath = path.substring(0, i);
+ String name = path.substring(i+1);
+ Collection collection = broker.openCollection(XmldbURI.create(folderPath), Lock.NO_LOCK);
+ if (collection == null) return false;
+ return collection.getDocument(broker, XmldbURI.create(name)) != null;
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ releaseBroker(broker);
+ }
+ }
+
+ /**
+ * Get the document for the given absolute path. Namespace bindings will be inherited
+ * from this database.
+ *
+ * @param path the absolute path of the desired document
+ * @return the document at the given path
+ * @throws DatabaseException if the document is not found or something else goes wrong
+ */
+ public Document getDocument(String path) {
+ if (path.length() == 0) throw new IllegalArgumentException("empty document path: " + path);
+ if (!path.startsWith("/")) throw new IllegalArgumentException("document path not absolute: " + path);
+ if (path.endsWith("/")) throw new IllegalArgumentException("document path ends with '/': " + path);
+ int i = path.lastIndexOf('/');
+ assert i != -1;
+ return getFolder(i == 0 ? "/" : path.substring(0, i)).documents().get(path.substring(i+1));
+ }
+
+ /**
+ * Get the folder for the given path. Namespace mappings will be inherited from this
+ * database.
+ *
+ * @param path the address of the desired collection
+ * @return a collection bound to the given path
+ * @throws DatabaseException if the path does not identify a valid collection
+ */
+ public Folder getFolder(String path) {
+ return new Folder(path, false, namespaceBindings.extend(), this);
+ }
+
+ /**
+ * Create the folder for the given path. Namespace mappings will be inherited from this
+ * database. If the folder does not exist, it is created along with all required ancestors.
+ *
+ * @param path the address of the desired collection
+ * @return a collection bound to the given path
+ */
+ public Folder createFolder(String path) {
+ return new Folder(path, true, namespaceBindings.extend(), this);
+ }
+
+ /**
+ * Return a query service that runs queries over the given list of resources.
+ * The resources can be of different kinds, and come from different locations in the
+ * folder hierarchy. The service will inherit the database's namespace bindings,
+ * rather than the bindings of any given context resource.
+ *
+ * @param context the arbitrary collection of database objects over which to query
+ * @return a query service over the given resources
+ */
+ public QueryService query(Resource... context) {
+ return query(Arrays.asList(context));
+ }
+
+ /**
+ * Return a query service that runs queries over the given list of resources.
+ * The resources can be of different kinds, and come from different locations in the
+ * folder hierarchy. The service will inherit the database's namespace bindings,
+ * rather than the bindings of any given context resource.
+ *
+ * @param context the arbitrary collection of database objects over which to query;
+ * the collection is not copied, and the collection's contents are re-read every time the query is performed
+ * @return a query service over the given resources
+ */
+ public QueryService query(final java.util.Collection extends Resource> context) {
+ return new QueryService(getFolder("/")) {
+ @Override void prepareContext(DBBroker broker) {
+ MutableDocumentSet mdocs = new DefaultDocumentSet();
+ base = new ValueSequence();
+ for (Resource res : context) {
+ QueryService qs = res.query();
+ if (qs.docs != null) mdocs.addAll(qs.docs);
+ if (qs.base != null) try {
+ base.addAll(qs.base);
+ } catch (XPathException e) {
+ throw new DatabaseException("unexpected item type conflict", e);
+ }
+ }
+ docs = mdocs;
+ }
+ };
+ }
+
+ /**
+ * Return a transaction for use with database operations. If a transaction is already in progress
+ * then join it, otherwise begin a new one. If a transaction is joined, calling commit
+ * or abort on the returned instance will have no effect; only the outermost
+ * transaction object can do this.
+ *
+ * @return a transaction object
+ */
+ static Transaction requireTransaction() {
+ Transaction t = localTransaction.get();
+ return t == null ? new Transaction(txManager, null) : new Transaction(t, null);
+ }
+
+ Transaction requireTransactionWithBroker() {
+ Transaction t = localTransaction.get();
+ return t == null ? new Transaction(txManager, this) : new Transaction(t, this);
+ }
+
+ void checkSame(Resource o) {
+ // allow other resource to be a NULL, as those are safe and database-neutral
+ if (!(o.database() == null || o.database().user == this.user)) throw new IllegalArgumentException("cannot combine objects from two database instances in one operation");
+ }
+
+ private static final WeakMultiValueHashMap staleMap = new WeakMultiValueHashMap();
+
+ private static void stale(String key) {
+ int updated = 0;
+ synchronized(staleMap) {
+ for (StaleMarker value : staleMap.get(key)) {value.mark(); updated++;}
+ staleMap.remove(key);
+ }
+ }
+
+ static void trackStale(String key, StaleMarker value) {
+ staleMap.put(normalizePath(key), value);
+ }
+
+ private static final ContentLoadingObserver contentObserver = new ContentLoadingObserver() {
+ public void dropIndex(Collection collection) {
+ stale(normalizePath(collection.getURI().getCollectionPath()));
+ }
+ public void dropIndex(DocumentImpl doc) throws ReadOnlyException {
+ stale(normalizePath(doc.getURI().getCollectionPath()));
+ }
+ public void removeNode(StoredNode node, NodePath currentPath, String content) {
+ stale(normalizePath(((DocumentImpl) node.getOwnerDocument()).getURI().getCollectionPath()) + "#" + node.getNodeId());
+ }
+ public void flush() {}
+ public void setDocument(DocumentImpl document) {}
+ public void storeAttribute(AttrImpl node, NodePath currentPath, int indexingHint, RangeIndexSpec spec, boolean remove) {}
+ public void storeText(TextImpl node, NodePath currentPath, int indexingHint) {}
+ public void sync() {}
+ public void printStatistics() {}
+
+ public boolean close() {return true;}
+ public void remove() {}
+ public void closeAndRemove() {
+ // TODO: do nothing OK here? indexes just got wiped and recreated, and this listener
+ // was removed...
+ }
+
+ };
+
+ static void queueDefrag(DocumentImpl doc) {
+ defragmenter.queue(doc);
+ }
+
+ private static final Defragmenter defragmenter = new Defragmenter();
+
+ private static class Defragmenter implements Runnable {
+ private static final Logger LOG = Logger.getLogger("org.exist.fluent.Database.defragmenter");
+ private static final long DEFRAG_INTERVAL = 10000; // ms
+ private Set docsToDefrag = new TreeSet();
+ private Thread thread;
+
+ public void start() {
+ if (thread != null) return;
+ thread = new Thread(this, "Database defragmenter");
+ thread.setPriority(Thread.NORM_PRIORITY-3);
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ public void stop() {
+ if (thread == null) return;
+ thread.interrupt();
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ // oh well
+ }
+ thread = null;
+ }
+
+ public synchronized void queue(DocumentImpl doc) {
+ docsToDefrag.add(doc);
+ }
+
+ public void run() {
+ while(true) {
+ try {
+ Thread.sleep(DEFRAG_INTERVAL);
+ } catch (InterruptedException e) {
+ break;
+ }
+
+ // Grab copy of docsToDefrag to avoid potential deadlocks (if an executing query has a lock on
+ // the document we want to defrag, we block, then if it tries to queue another document for
+ // defrag it blocks, and it's deadlock time).
+ Set docsToDefragCopy;
+ synchronized(this) {
+ LOG.debug(new MessageFormat(
+ "checking for documents to defragment, {0,choice,0#no candidates|1#1 candidate|1<{0,number,integer} candidates}")
+ .format(new Object[] {docsToDefrag.size()}));
+ docsToDefragCopy = docsToDefrag;
+ docsToDefrag = new TreeSet();
+ }
+
+ int count = 0;
+ try {
+ DBBroker broker = pool.get(pool.getSecurityManager().getSystemSubject());
+ try {
+ Integer fragmentationLimitObject = broker.getBrokerPool().getConfiguration().getInteger(DBBroker.PROPERTY_XUPDATE_FRAGMENTATION_FACTOR);
+ int fragmentationLimit = fragmentationLimitObject == null ? 0 : fragmentationLimitObject;
+ for (Iterator it = docsToDefragCopy.iterator(); it.hasNext(); ) {
+ DocumentImpl doc = it.next();
+ if (doc.getMetadata().getSplitCount() <= fragmentationLimit) {
+ it.remove();
+ } else {
+ // Must hold write lock on doc before checking stale map to avoid race condition
+ if (doc.getUpdateLock().attempt(Lock.WRITE_LOCK)) try {
+ String docPath = normalizePath(doc.getURI().getCollectionPath());
+ if (!staleMap.containsKey(docPath)) {
+ LOG.debug("defragmenting " + docPath);
+ count++;
+ Transaction tx = Database.requireTransaction();
+ try {
+ broker.defragXMLResource(tx.tx, doc);
+ tx.commit();
+ it.remove();
+ } finally {
+ tx.abortIfIncomplete();
+ }
+ }
+ } finally {
+ doc.getUpdateLock().release(Lock.WRITE_LOCK);
+ }
+ }
+ }
+ } finally {
+ pool.release(broker);
+ }
+ } catch (EXistException e) {
+ LOG.error("unable to get broker with system privileges to defragment documents", e);
+ }
+
+ LOG.debug(new MessageFormat(
+ "defragmented {0,choice,0#0 documents|1#1 document|1<{0,number,integer} documents}, next cycle in {1,number,integer}s")
+ .format(new Object[] {count, DEFRAG_INTERVAL / 1000}));
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ static Iterator emptyIterator() {
+ return EMPTY_ITERATOR;
+ }
+
+ @SuppressWarnings("unchecked")
+ static final Iterator EMPTY_ITERATOR = new Iterator() {
+ public boolean hasNext() {return false;}
+ public Object next() {throw new NoSuchElementException();}
+ public void remove() {throw new UnsupportedOperationException();}
+ };
+
+ @SuppressWarnings("unchecked")
+ static final Iterable EMPTY_ITERABLE = new Iterable() {
+ public Iterator iterator() {return EMPTY_ITERATOR;}
+ };
+
+}
diff --git a/extensions/fluent/src/org/exist/fluent/DatabaseException.java b/extensions/fluent/src/org/exist/fluent/DatabaseException.java
index 7c7d04594e6..b150fd34c7f 100644
--- a/extensions/fluent/src/org/exist/fluent/DatabaseException.java
+++ b/extensions/fluent/src/org/exist/fluent/DatabaseException.java
@@ -1,29 +1,29 @@
-package org.exist.fluent;
-
-/**
- * Signals all errors and failures related to database access.
- *
- * @author Piotr Kaminski
- * @version $Revision: 1.4 $ ($Date: 2006/04/13 19:12:17 $)
- */
-public class DatabaseException extends RuntimeException {
-
- private static final long serialVersionUID = -5795305208413473870L;
-
- DatabaseException() {
- super();
- }
-
- DatabaseException(String message) {
- super(message);
- }
-
- DatabaseException(Throwable cause) {
- super(cause);
- }
-
- DatabaseException(String message, Throwable cause) {
- super(message, cause);
- }
-
-}
+package org.exist.fluent;
+
+/**
+ * Signals all errors and failures related to database access.
+ *
+ * @author Piotr Kaminski
+ * @version $Revision: 1.4 $ ($Date: 2006/04/13 19:12:17 $)
+ */
+public class DatabaseException extends RuntimeException {
+
+ private static final long serialVersionUID = -5795305208413473870L;
+
+ DatabaseException() {
+ super();
+ }
+
+ DatabaseException(String message) {
+ super(message);
+ }
+
+ DatabaseException(Throwable cause) {
+ super(cause);
+ }
+
+ DatabaseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/extensions/fluent/src/org/exist/fluent/Document.java b/extensions/fluent/src/org/exist/fluent/Document.java
index 02c3d90a0c2..4591c52654b 100644
--- a/extensions/fluent/src/org/exist/fluent/Document.java
+++ b/extensions/fluent/src/org/exist/fluent/Document.java
@@ -1,382 +1,382 @@
-package org.exist.fluent;
-
-import java.io.*;
-import java.util.*;
-
-import org.exist.dom.*;
-import org.exist.security.Permission;
-import org.exist.storage.DBBroker;
-import org.exist.xquery.value.Sequence;
-
-/**
- * A document from the database, either binary or XML. Note that querying a non-XML
- * document is harmless, but will never return any results.
- *
- * @author Piotr Kaminski
- */
-public class Document extends NamedResource {
-
- /**
- * Listener for events affecting documents. The three possible actions are document
- * creation, update (modification), and deletion.
- *
- * @author Piotr Kaminski
- */
- public interface Listener extends org.exist.fluent.Listener {
- /**
- * Respond to a document event.
- *
- * @param ev the details of the event
- */
- void handle(Document.Event ev);
- }
-
- /**
- * An event that concerns a document.
- *
- * @author Piotr Kaminski
- */
- public static class Event extends org.exist.fluent.Listener.Event {
- /**
- * The document that's the subject of this event.
- * Note that for some timing/action combinations, this field might be null.
- */
- public final Document document;
-
- Event(Trigger trigger, String path, Document document) {
- super(trigger, path);
- this.document = document;
- }
-
- Event(ListenerManager.EventKey key, Document document) {
- super(key);
- this.document = document;
- }
-
- @Override
- public boolean equals(Object o) {
- return
- super.equals(o) &&
- (document == null ? ((Event) o).document == null : document.equals(((Event) o).document));
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() * 37 + (document == null ? 0 : document.hashCode());
- }
-
- @Override
- public String toString() {
- StringBuilder buf = new StringBuilder(super.toString());
- buf.insert(3, "Document.");
- buf.insert(buf.length()-1, ", " + document);
- return buf.toString();
- }
- }
-
- /**
- * The facet that gives access to a document's listeners.
- *
- * @author Piotr Kaminski
- */
- public class ListenersFacet {
-
- /**
- * Add a listener for this document. Equivalent to add(EnumSet.of(trigger), listener).
- *
- * @see #add(Set, Document.Listener)
- * @param trigger the kind of event the listener should be notified of
- * @param listener the listener to notify of events
- */
- public void add(Trigger trigger, Document.Listener listener) {
- add(EnumSet.of(trigger), listener);
- }
-
- /**
- * Add a listener for this document.
- *
- * @param triggers the kinds of events the listener should be notified of; the set must not be empty
- * @param listener the listener to notify of events
- */
- public void add(Set triggers, Document.Listener listener) {
- staleMarker.check();
- ListenerManager.INSTANCE.add(path(), ListenerManager.Depth.ZERO, triggers, listener, Document.this);
- }
-
- /**
- * Remove a listener previously added through this facet. This will remove the listener from
- * all combinations of timing and action for this document, even if added via multiple invocations
- * of the add methods. However, it will not remove the listener from combinations
- * added through other facets.
- *
- * @param listener the listener to remove
- */
- public void remove(Document.Listener listener) {
- // don't check for staleness here, might still want to remove listeners after doc is gone
- ListenerManager.INSTANCE.remove(path(), ListenerManager.Depth.ZERO, listener);
- }
- }
-
- /**
- * The metadata facet for this document. Allows access to and manipulation of various aspects
- * of the document's metadata, including its permissions and various timestamps.
- * NOTE: The interface is fairly bare-bones right now, until I figure out the use cases and flesh
- * it out a bit.
- */
- public static class MetadataFacet extends NamedResource.MetadataFacet {
- private final DocumentMetadata docMetadata;
- private MetadataFacet(Permission permissions, DocumentMetadata docMetadata, Database db) {
- super(permissions, db);
- this.docMetadata = docMetadata;
- }
- @Override public Date creationDate() {return new Date(docMetadata.getCreated());}
-
- /**
- * Return the time at which this document was last modified.
- *
- * @return the date of the last modification
- */
- public Date lastModificationDate() {return new Date(docMetadata.getLastModified());}
-
- /**
- * Return the recorded MIME type of this document.
- *
- * @return this document's MIME type
- */
- public String mimeType() {return docMetadata.getMimeType();}
-
- /**
- * Set the MIME type of this document.
- *
- * @param mimeType this document's desired MIME type
- */
- public void setMimeType(String mimeType) {docMetadata.setMimeType(mimeType);}
- }
-
- protected DocumentImpl doc;
- protected StaleMarker staleMarker;
- private ListenersFacet listeners;
- private MetadataFacet metadata;
-
- Document(DocumentImpl dimpl, NamespaceMap namespaceBindings, Database db) {
- super(namespaceBindings, db);
- changeDoc(dimpl);
- }
-
- private void changeDoc(DocumentImpl dimpl) {
- if (dimpl == null) throw new NullPointerException("no such document");
- assert getClass() == (dimpl instanceof BinaryDocument ? Document.class : XMLDocument.class);
- this.doc = dimpl;
- String path = dimpl.getURI().getCollectionPath();
- staleMarker = new StaleMarker();
- staleMarker.track(path.substring(0, path.lastIndexOf('/'))); // folder
- staleMarker.track(path); // document
- }
-
- static Document newInstance(DocumentImpl dimpl, Resource origin) {
- return newInstance(dimpl, origin.namespaceBindings().extend(), origin.database());
- }
-
- static Document newInstance(DocumentImpl dimpl, NamespaceMap namespaceBindings, Database db) {
- return dimpl instanceof BinaryDocument ? new Document(dimpl, namespaceBindings, db) : new XMLDocument(dimpl, namespaceBindings, db);
- }
-
- @Override Sequence convertToSequence() {
- // TODO: figure out if binary documents can be converted after all
- throw new UnsupportedOperationException("binary resources are not convertible");
- }
-
- /**
- * Return the listeners facet for this document, used for adding and removing document listeners.
- *
- * @return the listeners facet for this document
- */
- public ListenersFacet listeners() {
- if (listeners == null) listeners = new ListenersFacet();
- return listeners;
- }
-
- @Override public MetadataFacet metadata() {
- if (metadata == null) metadata = new MetadataFacet(doc.getPermissions(), doc.getMetadata(), db);
- return metadata;
- }
-
- /**
- * Cast this document to an {@link XMLDocument}, if possible.
- *
- * @return this document cast as an XML document
- * @throws DatabaseException if this document is not an XML document
- */
- public XMLDocument xml() {
- throw new DatabaseException("document is not XML");
- }
-
- @Override public boolean equals(Object o) {
- if (o instanceof Document) return doc.getDocId() == ((Document) o).doc.getDocId();
- return false;
- }
-
- @Override public int hashCode() {
- return doc.getDocId();
- }
-
- /**
- * Return a string representation of the reference to this document. The representation will
- * list the document's path, but will not include its contents.
- *
- * @return a string representation of this document
- */
- @Override public String toString() {
- return "document '" + path() + "'";
- }
-
- /**
- * Return the local filename of this document. This name will never contain
- * slashes ('/').
- *
- * @return the local filename of this document
- */
- @Override public String name() {
- return doc.getFileURI().getCollectionPath();
- }
-
- /**
- * Return the full path of this document. This is the path of its parent folder plus its
- * filename.
- *
- * @return the full path of this document
- */
- @Override public String path() {
- // TODO: is this check necessary?
- // if (doc.getURI() == null) throw new DatabaseException("handle invalid, document may have been deleted");
- return Database.normalizePath(doc.getURI().getCollectionPath());
- }
-
- /**
- * Return the folder that contains this document.
- *
- * @return the folder that contains this document
- */
- public Folder folder() {
- staleMarker.check();
- String path = path();
- int i = path.lastIndexOf('/');
- assert i != -1;
- return new Folder(i == 0 ? "/" : path.substring(0, i), false, this);
- }
-
- /**
- * Return the length of this document, in bytes. For binary documents, this is the actual
- * size of the file; for XML documents, this is the approximate amount of space that the
- * document occupies in the database, and is unrelated to its serialized length.
- *
- * @return the length of this document, in bytes
- */
- public long length() {
- return doc.getContentLength();
- }
-
- /**
- * Delete this document from the database.
- */
- @Override public void delete() {
- staleMarker.check();
- folder().removeDocument(doc);
- }
-
- /**
- * Copy this document to another collection, potentially changing the copy's name in the process.
- * @see Name
- *
- * @param destination the destination folder for the copy
- * @param name the desired name for the copy
- * @return the new copy of the document
- */
- @Override public Document copy(Folder destination, Name name) {
- return newInstance(moveOrCopy(destination, name, true), this);
- }
-
- /**
- * Move this document to another collection, potentially changing its name in the process.
- * This document will refer to the document in its new location after this method returns.
- * You can easily use this method to move a document without changing its name
- * (doc.move(newFolder, Name.keepCreate())) or to rename a document
- * without changing its location (doc.move(doc.folder(), Name.create(newName))).
- * @see Name
- *
- * @param destination the destination folder for the move
- * @param name the desired name for the moved document
- */
- @Override public void move(Folder destination, Name name) {
- changeDoc(moveOrCopy(destination, name, false));
- }
-
- private DocumentImpl moveOrCopy(Folder destination, Name name, boolean copy) {
- db.checkSame(destination);
- staleMarker.check();
- name.setOldName(name());
- return destination.moveOrCopyDocument(doc, name, copy);
- }
-
- /**
- * Return the contents of this document interpreted as a string. Binary documents are
- * decoded using the default character encoding specified for the database.
- *
- * @see Database#setDefaultCharacterEncoding(String)
- * @return the contents of this document
- * @throws DatabaseException if the encoding is not supported or some other unexpected IOException occurs
- */
- public String contentsAsString() {
- DBBroker broker = db.acquireBroker();
- try {
- InputStream is = broker.getBinaryResource((BinaryDocument) doc);
- byte [] data = new byte[(int)broker.getBinaryResourceSize((BinaryDocument) doc)];
- is.read(data);
- is.close();
- return new String(data, db.defaultCharacterEncoding);
- } catch (UnsupportedEncodingException e) {
- throw new DatabaseException(e);
- } catch (IOException e) {
- throw new DatabaseException(e);
- } finally {
- db.releaseBroker(broker);
- }
- }
-
- /**
- * Export this document to the given file, overwriting it if it already exists.
- *
- * @param destination the file to export to
- * @throws IOException if the export failed due to an I/O error
- */
- public void export(File destination) throws IOException {
- OutputStream stream = new BufferedOutputStream(new FileOutputStream(destination));
- try {
- write(stream);
- } finally {
- stream.close();
- }
- }
-
- /**
- * Copy the contents of the document to the given stream. XML documents will use
- * the default character encoding set for the database.
- * @see Database#setDefaultCharacterEncoding(String)
- *
- * @param stream the output stream to copy the document to
- * @throws IOException in case of I/O problems;
- * WARNING: I/O exceptions are currently logged and eaten by eXist, so they won't propagate to this layer!
- */
- public void write(OutputStream stream) throws IOException {
- DBBroker broker = db.acquireBroker();
- try {
- broker.readBinaryResource((BinaryDocument) doc, stream);
- } finally {
- db.releaseBroker(broker);
- }
- }
-
- @Override QueryService createQueryService() {
- return QueryService.NULL;
- }
-}
+package org.exist.fluent;
+
+import java.io.*;
+import java.util.*;
+
+import org.exist.dom.*;
+import org.exist.security.Permission;
+import org.exist.storage.DBBroker;
+import org.exist.xquery.value.Sequence;
+
+/**
+ * A document from the database, either binary or XML. Note that querying a non-XML
+ * document is harmless, but will never return any results.
+ *
+ * @author Piotr Kaminski
+ */
+public class Document extends NamedResource {
+
+ /**
+ * Listener for events affecting documents. The three possible actions are document
+ * creation, update (modification), and deletion.
+ *
+ * @author Piotr Kaminski
+ */
+ public interface Listener extends org.exist.fluent.Listener {
+ /**
+ * Respond to a document event.
+ *
+ * @param ev the details of the event
+ */
+ void handle(Document.Event ev);
+ }
+
+ /**
+ * An event that concerns a document.
+ *
+ * @author Piotr Kaminski
+ */
+ public static class Event extends org.exist.fluent.Listener.Event {
+ /**
+ * The document that's the subject of this event.
+ * Note that for some timing/action combinations, this field might be null.
+ */
+ public final Document document;
+
+ Event(Trigger trigger, String path, Document document) {
+ super(trigger, path);
+ this.document = document;
+ }
+
+ Event(ListenerManager.EventKey key, Document document) {
+ super(key);
+ this.document = document;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return
+ super.equals(o) &&
+ (document == null ? ((Event) o).document == null : document.equals(((Event) o).document));
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() * 37 + (document == null ? 0 : document.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder(super.toString());
+ buf.insert(3, "Document.");
+ buf.insert(buf.length()-1, ", " + document);
+ return buf.toString();
+ }
+ }
+
+ /**
+ * The facet that gives access to a document's listeners.
+ *
+ * @author Piotr Kaminski
+ */
+ public class ListenersFacet {
+
+ /**
+ * Add a listener for this document. Equivalent to add(EnumSet.of(trigger), listener).
+ *
+ * @see #add(Set, Document.Listener)
+ * @param trigger the kind of event the listener should be notified of
+ * @param listener the listener to notify of events
+ */
+ public void add(Trigger trigger, Document.Listener listener) {
+ add(EnumSet.of(trigger), listener);
+ }
+
+ /**
+ * Add a listener for this document.
+ *
+ * @param triggers the kinds of events the listener should be notified of; the set must not be empty
+ * @param listener the listener to notify of events
+ */
+ public void add(Set triggers, Document.Listener listener) {
+ staleMarker.check();
+ ListenerManager.INSTANCE.add(path(), ListenerManager.Depth.ZERO, triggers, listener, Document.this);
+ }
+
+ /**
+ * Remove a listener previously added through this facet. This will remove the listener from
+ * all combinations of timing and action for this document, even if added via multiple invocations
+ * of the add methods. However, it will not remove the listener from combinations
+ * added through other facets.
+ *
+ * @param listener the listener to remove
+ */
+ public void remove(Document.Listener listener) {
+ // don't check for staleness here, might still want to remove listeners after doc is gone
+ ListenerManager.INSTANCE.remove(path(), ListenerManager.Depth.ZERO, listener);
+ }
+ }
+
+ /**
+ * The metadata facet for this document. Allows access to and manipulation of various aspects
+ * of the document's metadata, including its permissions and various timestamps.
+ * NOTE: The interface is fairly bare-bones right now, until I figure out the use cases and flesh
+ * it out a bit.
+ */
+ public static class MetadataFacet extends NamedResource.MetadataFacet {
+ private final DocumentMetadata docMetadata;
+ private MetadataFacet(Permission permissions, DocumentMetadata docMetadata, Database db) {
+ super(permissions, db);
+ this.docMetadata = docMetadata;
+ }
+ @Override public Date creationDate() {return new Date(docMetadata.getCreated());}
+
+ /**
+ * Return the time at which this document was last modified.
+ *
+ * @return the date of the last modification
+ */
+ public Date lastModificationDate() {return new Date(docMetadata.getLastModified());}
+
+ /**
+ * Return the recorded MIME type of this document.
+ *
+ * @return this document's MIME type
+ */
+ public String mimeType() {return docMetadata.getMimeType();}
+
+ /**
+ * Set the MIME type of this document.
+ *
+ * @param mimeType this document's desired MIME type
+ */
+ public void setMimeType(String mimeType) {docMetadata.setMimeType(mimeType);}
+ }
+
+ protected DocumentImpl doc;
+ protected StaleMarker staleMarker;
+ private ListenersFacet listeners;
+ private MetadataFacet metadata;
+
+ Document(DocumentImpl dimpl, NamespaceMap namespaceBindings, Database db) {
+ super(namespaceBindings, db);
+ changeDoc(dimpl);
+ }
+
+ private void changeDoc(DocumentImpl dimpl) {
+ if (dimpl == null) throw new NullPointerException("no such document");
+ assert getClass() == (dimpl instanceof BinaryDocument ? Document.class : XMLDocument.class);
+ this.doc = dimpl;
+ String path = dimpl.getURI().getCollectionPath();
+ staleMarker = new StaleMarker();
+ staleMarker.track(path.substring(0, path.lastIndexOf('/'))); // folder
+ staleMarker.track(path); // document
+ }
+
+ static Document newInstance(DocumentImpl dimpl, Resource origin) {
+ return newInstance(dimpl, origin.namespaceBindings().extend(), origin.database());
+ }
+
+ static Document newInstance(DocumentImpl dimpl, NamespaceMap namespaceBindings, Database db) {
+ return dimpl instanceof BinaryDocument ? new Document(dimpl, namespaceBindings, db) : new XMLDocument(dimpl, namespaceBindings, db);
+ }
+
+ @Override Sequence convertToSequence() {
+ // TODO: figure out if binary documents can be converted after all
+ throw new UnsupportedOperationException("binary resources are not convertible");
+ }
+
+ /**
+ * Return the listeners facet for this document, used for adding and removing document listeners.
+ *
+ * @return the listeners facet for this document
+ */
+ public ListenersFacet listeners() {
+ if (listeners == null) listeners = new ListenersFacet();
+ return listeners;
+ }
+
+ @Override public MetadataFacet metadata() {
+ if (metadata == null) metadata = new MetadataFacet(doc.getPermissions(), doc.getMetadata(), db);
+ return metadata;
+ }
+
+ /**
+ * Cast this document to an {@link XMLDocument}, if possible.
+ *
+ * @return this document cast as an XML document
+ * @throws DatabaseException if this document is not an XML document
+ */
+ public XMLDocument xml() {
+ throw new DatabaseException("document is not XML");
+ }
+
+ @Override public boolean equals(Object o) {
+ if (o instanceof Document) return doc.getDocId() == ((Document) o).doc.getDocId();
+ return false;
+ }
+
+ @Override public int hashCode() {
+ return doc.getDocId();
+ }
+
+ /**
+ * Return a string representation of the reference to this document. The representation will
+ * list the document's path, but will not include its contents.
+ *
+ * @return a string representation of this document
+ */
+ @Override public String toString() {
+ return "document '" + path() + "'";
+ }
+
+ /**
+ * Return the local filename of this document. This name will never contain
+ * slashes ('/').
+ *
+ * @return the local filename of this document
+ */
+ @Override public String name() {
+ return doc.getFileURI().getCollectionPath();
+ }
+
+ /**
+ * Return the full path of this document. This is the path of its parent folder plus its
+ * filename.
+ *
+ * @return the full path of this document
+ */
+ @Override public String path() {
+ // TODO: is this check necessary?
+ // if (doc.getURI() == null) throw new DatabaseException("handle invalid, document may have been deleted");
+ return Database.normalizePath(doc.getURI().getCollectionPath());
+ }
+
+ /**
+ * Return the folder that contains this document.
+ *
+ * @return the folder that contains this document
+ */
+ public Folder folder() {
+ staleMarker.check();
+ String path = path();
+ int i = path.lastIndexOf('/');
+ assert i != -1;
+ return new Folder(i == 0 ? "/" : path.substring(0, i), false, this);
+ }
+
+ /**
+ * Return the length of this document, in bytes. For binary documents, this is the actual
+ * size of the file; for XML documents, this is the approximate amount of space that the
+ * document occupies in the database, and is unrelated to its serialized length.
+ *
+ * @return the length of this document, in bytes
+ */
+ public long length() {
+ return doc.getContentLength();
+ }
+
+ /**
+ * Delete this document from the database.
+ */
+ @Override public void delete() {
+ staleMarker.check();
+ folder().removeDocument(doc);
+ }
+
+ /**
+ * Copy this document to another collection, potentially changing the copy's name in the process.
+ * @see Name
+ *
+ * @param destination the destination folder for the copy
+ * @param name the desired name for the copy
+ * @return the new copy of the document
+ */
+ @Override public Document copy(Folder destination, Name name) {
+ return newInstance(moveOrCopy(destination, name, true), this);
+ }
+
+ /**
+ * Move this document to another collection, potentially changing its name in the process.
+ * This document will refer to the document in its new location after this method returns.
+ * You can easily use this method to move a document without changing its name
+ * (doc.move(newFolder, Name.keepCreate())) or to rename a document
+ * without changing its location (doc.move(doc.folder(), Name.create(newName))).
+ * @see Name
+ *
+ * @param destination the destination folder for the move
+ * @param name the desired name for the moved document
+ */
+ @Override public void move(Folder destination, Name name) {
+ changeDoc(moveOrCopy(destination, name, false));
+ }
+
+ private DocumentImpl moveOrCopy(Folder destination, Name name, boolean copy) {
+ db.checkSame(destination);
+ staleMarker.check();
+ name.setOldName(name());
+ return destination.moveOrCopyDocument(doc, name, copy);
+ }
+
+ /**
+ * Return the contents of this document interpreted as a string. Binary documents are
+ * decoded using the default character encoding specified for the database.
+ *
+ * @see Database#setDefaultCharacterEncoding(String)
+ * @return the contents of this document
+ * @throws DatabaseException if the encoding is not supported or some other unexpected IOException occurs
+ */
+ public String contentsAsString() {
+ DBBroker broker = db.acquireBroker();
+ try {
+ InputStream is = broker.getBinaryResource((BinaryDocument) doc);
+ byte [] data = new byte[(int)broker.getBinaryResourceSize((BinaryDocument) doc)];
+ is.read(data);
+ is.close();
+ return new String(data, db.defaultCharacterEncoding);
+ } catch (UnsupportedEncodingException e) {
+ throw new DatabaseException(e);
+ } catch (IOException e) {
+ throw new DatabaseException(e);
+ } finally {
+ db.releaseBroker(broker);
+ }
+ }
+
+ /**
+ * Export this document to the given file, overwriting it if it already exists.
+ *
+ * @param destination the file to export to
+ * @throws IOException if the export failed due to an I/O error
+ */
+ public void export(File destination) throws IOException {
+ OutputStream stream = new BufferedOutputStream(new FileOutputStream(destination));
+ try {
+ write(stream);
+ } finally {
+ stream.close();
+ }
+ }
+
+ /**
+ * Copy the contents of the document to the given stream. XML documents will use
+ * the default character encoding set for the database.
+ * @see Database#setDefaultCharacterEncoding(String)
+ *
+ * @param stream the output stream to copy the document to
+ * @throws IOException in case of I/O problems;
+ * WARNING: I/O exceptions are currently logged and eaten by eXist, so they won't propagate to this layer!
+ */
+ public void write(OutputStream stream) throws IOException {
+ DBBroker broker = db.acquireBroker();
+ try {
+ broker.readBinaryResource((BinaryDocument) doc, stream);
+ } finally {
+ db.releaseBroker(broker);
+ }
+ }
+
+ @Override QueryService createQueryService() {
+ return QueryService.NULL;
+ }
+}
diff --git a/extensions/fluent/src/org/exist/fluent/ElementBuilder.java b/extensions/fluent/src/org/exist/fluent/ElementBuilder.java
index 7620aa140b7..cc7beaee669 100644
--- a/extensions/fluent/src/org/exist/fluent/ElementBuilder.java
+++ b/extensions/fluent/src/org/exist/fluent/ElementBuilder.java
@@ -1,320 +1,320 @@
-package org.exist.fluent;
-
-import java.util.*;
-
-import javax.xml.parsers.*;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.*;
-import org.w3c.dom.Node;
-
-/**
- * A builder of DOM trees, meant to be either stand alone or be inserted into
- * pre-existing ones. Cannot remove nodes from the base tree. You must {@link #commit()}
- * the builder to persist the recorded changes in the database.
- *
- * All the nodes in the tree being built must be from the same implementation. If you attempt
- * to add foreign nodes -- for example, persistent nodes from the database -- using the
- * {@link #node} or {@link #nodes} methods to a builder that already has temporary nodes
- * built using its other methods, they'll be imported. Importing nodes will usually make a deep
- * copy of the tree in memory, which could cause problems if you're trying to add a big stored
- * node tree.
- *
- * @param the type of object returned upon completion of the builder,
- * depends on the context in which the builder is used
- *
- * @author Piotr Kaminski
- * @version $Revision: 1.18 $ ($Date: 2006/04/13 19:12:16 $)
- */
-public class ElementBuilder {
-
- interface CompletedCallback {
- public T completed(Node[] nodes);
- }
-
- private static final Logger LOG = Logger.getLogger(ElementBuilder.class);
-
- private /* final */ CompletedCallback callback;
- private final boolean allowFragment;
- private final List stack = new LinkedList();
- private final List top = new LinkedList();
- private boolean done;
- private final org.w3c.dom.Document doc;
- private NamespaceMap namespaceBindings;
-
- static org.w3c.dom.Document createDocumentNode() {
- try {
- DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
- dbf.setNamespaceAware(true);
- return dbf.newDocumentBuilder().newDocument();
- } catch (ParserConfigurationException e) {
- throw new RuntimeException("unable to create new memory DOM", e);
- }
- }
-
- private class ScratchCallback implements CompletedCallback {
- public Node completed(Node[] nodes) {
- if (nodes.length == 1) return nodes[0];
- DocumentFragment frag = doc.createDocumentFragment();
- for (int i = 0; i < nodes.length; i++) {
- frag.appendChild(adopt(nodes[i]));
- }
- return frag;
- }
- }
-
- /**
- * Create a new element builder that creates an in-memory DOM tree and
- * returns the top node (possibly a fragment node) upon commit.
- *
- * @param namespaceBindings the namespace bindings to use, or null for none
- * @return a scratch in-memory builder
- */
- public static ElementBuilder createScratch(NamespaceMap namespaceBindings) {
- ElementBuilder builder = new ElementBuilder(namespaceBindings, true, null);
- builder.setCallback(builder.new ScratchCallback());
- return builder;
- }
-
- ElementBuilder(NamespaceMap namespaceBindings, boolean allowFragment, CompletedCallback callback) {
- this.callback = callback;
- this.allowFragment = allowFragment;
- this.namespaceBindings = namespaceBindings == null ? new NamespaceMap() : namespaceBindings.extend();
- this.doc = createDocumentNode();
- }
-
- private void setCallback(CompletedCallback callback) {
- this.callback = callback;
- }
-
- private Node current() {
- if (stack.isEmpty()) throw new IllegalStateException("no current node");
- return stack.get(0);
- }
-
- private void checkDone() {
- if (done) throw new IllegalStateException("builder already done");
- }
-
- /**
- * Insert a namespace binding scoped to this builder only.
- *
- * @param key the prefix to bind
- * @param uri the namespace uri
- * @return this element builder, for chaining calls
- */
- public ElementBuilder namespace(String key, String uri) {
- namespaceBindings.put(key, uri);
- return this;
- }
-
- /**
- * Insert a copy of the given node.
- *
- * @param node the node to insert
- * @return this element builder, for chaining calls
- */
- public ElementBuilder node(org.exist.fluent.Node node) {
- return node(node.getDOMNode());
- }
-
- /**
- * Insert copies of the given nodes.
- *
- * @param nodes the nodes to insert
- * @return this element builder, for chaining calls
- */
- public ElementBuilder nodes(ItemList.NodesFacet nodes) {
- for (org.exist.fluent.Node node : nodes) node(node);
- return this;
- }
-
- /**
- * Insert a copy of the given node. The node can be an element node, a text node,
- * a document node or a fragment node. In the case of the latter two, their children
- * are inserted instead.
- *
- * @param node the node to insert
- * @return this element builder, for chaining calls
- */
- public ElementBuilder node(Node node) {
- checkDone();
- if (node instanceof org.exist.memtree.NodeImpl) ((org.exist.memtree.NodeImpl) node).expand();
-
- switch (node.getNodeType()) {
-
- case Node.DOCUMENT_NODE:
- case Node.DOCUMENT_FRAGMENT_NODE: {
- NodeList children = node.getChildNodes();
- for (int i=0; i elem(String tag) {
- checkDone();
- Element elem = QName.parse(tag, namespaceBindings).createElement(doc);
- appendElem(elem);
- stack.add(0, elem);
- return this;
- }
-
- private Node adopt(Node node) {
- if (node.getOwnerDocument() == doc) return node;
- if (node.getParentNode() == null) try {
- Node result = doc.adoptNode(node);
- if (result != null) return result;
- } catch (DOMException e) {}
- return doc.importNode(node, true);
- }
-
- private void appendElem(Element elem) {
- if (stack.isEmpty()) {
- if (!allowFragment && !top.isEmpty()) throw new IllegalStateException("unable to build document fragment with multiple root nodes in current context");
- top.add(elem);
- } else {
- current().appendChild(adopt(elem));
- }
- }
-
- /**
- * Close the currently open element, matching it to the given tag.
- *
- * @param tag the tag of the element to be ended
- * @return this element builder, for chaining calls
- */
- public ElementBuilder end(String tag) {
- checkDone();
- try {
- Element elem = stack.remove(0);
- QName elemName = QName.of(elem);
- if (!elemName.equals(QName.parse(tag, namespaceBindings))) throw new IllegalStateException("element on top of stack is '" + elemName + "' not '" + tag + "'");
- return this;
- } catch (IndexOutOfBoundsException e) {
- throw new IllegalStateException("no open elements to match end(" + tag + ")");
- }
- }
-
- /**
- * Close the currently open element, matching it to one of the given tags. Use
- * this method when the element could be one of many, but it's not convenient to remember
- * precisely which element name was used. This is still safer than just popping the top element
- * off arbitrarily.
- *
- * @param tags the possible tags of the element to be ended
- * @return this element builder, for chaining calls
- */
- public ElementBuilder end(String... tags) {
- checkDone();
- try {
- Element elem = stack.remove(0);
- QName elemName = QName.of(elem);
- boolean matched = false;
- for (String tag : tags) {
- if (elemName.equals(QName.parse(tag, namespaceBindings))) {
- matched = true;
- break;
- }
- }
- if (!matched) throw new IllegalStateException("element on top of stack is '" + elemName + "' not one of " + Arrays.asList(tags));
- return this;
- } catch (IndexOutOfBoundsException e) {
- throw new IllegalStateException("no open elements to match end(" + Arrays.asList(tags) + ")");
- }
- }
-
- /**
- * Add an attribute to the currently open element. If an attribute with the same name was
- * previously added to the element, overwrite its value.
- *
- * @param name the name of the attribute to add
- * @param value the value of the attribute, will be converted to a string using {@link DataUtils#toXMLString(Object)}
- * @return this element builder, for chaining calls
- */
- public ElementBuilder attr(String name, Object value) {
- checkDone();
- if (current().getNodeType() != Node.ELEMENT_NODE) throw new IllegalStateException("current node is not an element");
- QName.parse(name, namespaceBindings, null).setAttribute((Element) current(), DataUtils.toXMLString(value));
- return this;
- }
-
- /**
- * Add an attribute to the currently open element if the given condition holds. If condition
- * is true, this behaves as {@link #attr(String, Object)}, otherwise it does nothing.
- *
- * @param condition the condition to satisfy before adding the attribute
- * @param name the name of the attribute to add
- * @param value the value of the attribute
- * @return the element builder, for chaining calls
- */
- public ElementBuilder attrIf(boolean condition, String name, Object value) {
- if (condition) attr(name, value);
- return this;
- }
-
- /**
- * Insert text into the currenly open element.
- *
- * @param text the text to insert
- * @return this element builder, for chaining calls
- */
- public ElementBuilder text(Object text) {
- checkDone();
- appendText(DataUtils.toXMLString(text));
- return this;
- }
-
- private void appendText(String text) {
- Node textNode = doc.createTextNode(text);
- if (stack.isEmpty()) {
- if (!allowFragment) throw new IllegalStateException("unable to add a root text node in current context");
- top.add(textNode);
- } else {
- if (current().getNodeType() != Node.ELEMENT_NODE) throw new IllegalStateException("current node is not an element");
- current().appendChild(textNode);
- }
- }
-
- /**
- * Commit this element builder, persisting the recorded elements into the database.
- *
- * @return the newly created resource, as appropriate
- */
- public K commit() {
- checkDone();
- done = true;
- if (stack.size() != 0) throw new IllegalStateException("can't commit with " + stack.size() + " elements left open");
- if (top.isEmpty()) return null;
- return callback.completed(top.toArray(new Node[top.size()]));
- }
-
- @Override
- protected void finalize() {
- if (!done) LOG.warn("disposed without commit");
- }
-
-
-}
+package org.exist.fluent;
+
+import java.util.*;
+
+import javax.xml.parsers.*;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.*;
+import org.w3c.dom.Node;
+
+/**
+ * A builder of DOM trees, meant to be either stand alone or be inserted into
+ * pre-existing ones. Cannot remove nodes from the base tree. You must {@link #commit()}
+ * the builder to persist the recorded changes in the database.
+ *
+ * All the nodes in the tree being built must be from the same implementation. If you attempt
+ * to add foreign nodes -- for example, persistent nodes from the database -- using the
+ * {@link #node} or {@link #nodes} methods to a builder that already has temporary nodes
+ * built using its other methods, they'll be imported. Importing nodes will usually make a deep
+ * copy of the tree in memory, which could cause problems if you're trying to add a big stored
+ * node tree.
+ *
+ * @param the type of object returned upon completion of the builder,
+ * depends on the context in which the builder is used
+ *
+ * @author Piotr Kaminski
+ * @version $Revision: 1.18 $ ($Date: 2006/04/13 19:12:16 $)
+ */
+public class ElementBuilder {
+
+ interface CompletedCallback {
+ public T completed(Node[] nodes);
+ }
+
+ private static final Logger LOG = Logger.getLogger(ElementBuilder.class);
+
+ private /* final */ CompletedCallback callback;
+ private final boolean allowFragment;
+ private final List stack = new LinkedList();
+ private final List top = new LinkedList();
+ private boolean done;
+ private final org.w3c.dom.Document doc;
+ private NamespaceMap namespaceBindings;
+
+ static org.w3c.dom.Document createDocumentNode() {
+ try {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ return dbf.newDocumentBuilder().newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException("unable to create new memory DOM", e);
+ }
+ }
+
+ private class ScratchCallback implements CompletedCallback {
+ public Node completed(Node[] nodes) {
+ if (nodes.length == 1) return nodes[0];
+ DocumentFragment frag = doc.createDocumentFragment();
+ for (int i = 0; i < nodes.length; i++) {
+ frag.appendChild(adopt(nodes[i]));
+ }
+ return frag;
+ }
+ }
+
+ /**
+ * Create a new element builder that creates an in-memory DOM tree and
+ * returns the top node (possibly a fragment node) upon commit.
+ *
+ * @param namespaceBindings the namespace bindings to use, or null for none
+ * @return a scratch in-memory builder
+ */
+ public static ElementBuilder createScratch(NamespaceMap namespaceBindings) {
+ ElementBuilder builder = new ElementBuilder(namespaceBindings, true, null);
+ builder.setCallback(builder.new ScratchCallback());
+ return builder;
+ }
+
+ ElementBuilder(NamespaceMap namespaceBindings, boolean allowFragment, CompletedCallback callback) {
+ this.callback = callback;
+ this.allowFragment = allowFragment;
+ this.namespaceBindings = namespaceBindings == null ? new NamespaceMap() : namespaceBindings.extend();
+ this.doc = createDocumentNode();
+ }
+
+ private void setCallback(CompletedCallback callback) {
+ this.callback = callback;
+ }
+
+ private Node current() {
+ if (stack.isEmpty()) throw new IllegalStateException("no current node");
+ return stack.get(0);
+ }
+
+ private void checkDone() {
+ if (done) throw new IllegalStateException("builder already done");
+ }
+
+ /**
+ * Insert a namespace binding scoped to this builder only.
+ *
+ * @param key the prefix to bind
+ * @param uri the namespace uri
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder namespace(String key, String uri) {
+ namespaceBindings.put(key, uri);
+ return this;
+ }
+
+ /**
+ * Insert a copy of the given node.
+ *
+ * @param node the node to insert
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder node(org.exist.fluent.Node node) {
+ return node(node.getDOMNode());
+ }
+
+ /**
+ * Insert copies of the given nodes.
+ *
+ * @param nodes the nodes to insert
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder nodes(ItemList.NodesFacet nodes) {
+ for (org.exist.fluent.Node node : nodes) node(node);
+ return this;
+ }
+
+ /**
+ * Insert a copy of the given node. The node can be an element node, a text node,
+ * a document node or a fragment node. In the case of the latter two, their children
+ * are inserted instead.
+ *
+ * @param node the node to insert
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder node(Node node) {
+ checkDone();
+ if (node instanceof org.exist.memtree.NodeImpl) ((org.exist.memtree.NodeImpl) node).expand();
+
+ switch (node.getNodeType()) {
+
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE: {
+ NodeList children = node.getChildNodes();
+ for (int i=0; i elem(String tag) {
+ checkDone();
+ Element elem = QName.parse(tag, namespaceBindings).createElement(doc);
+ appendElem(elem);
+ stack.add(0, elem);
+ return this;
+ }
+
+ private Node adopt(Node node) {
+ if (node.getOwnerDocument() == doc) return node;
+ if (node.getParentNode() == null) try {
+ Node result = doc.adoptNode(node);
+ if (result != null) return result;
+ } catch (DOMException e) {}
+ return doc.importNode(node, true);
+ }
+
+ private void appendElem(Element elem) {
+ if (stack.isEmpty()) {
+ if (!allowFragment && !top.isEmpty()) throw new IllegalStateException("unable to build document fragment with multiple root nodes in current context");
+ top.add(elem);
+ } else {
+ current().appendChild(adopt(elem));
+ }
+ }
+
+ /**
+ * Close the currently open element, matching it to the given tag.
+ *
+ * @param tag the tag of the element to be ended
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder end(String tag) {
+ checkDone();
+ try {
+ Element elem = stack.remove(0);
+ QName elemName = QName.of(elem);
+ if (!elemName.equals(QName.parse(tag, namespaceBindings))) throw new IllegalStateException("element on top of stack is '" + elemName + "' not '" + tag + "'");
+ return this;
+ } catch (IndexOutOfBoundsException e) {
+ throw new IllegalStateException("no open elements to match end(" + tag + ")");
+ }
+ }
+
+ /**
+ * Close the currently open element, matching it to one of the given tags. Use
+ * this method when the element could be one of many, but it's not convenient to remember
+ * precisely which element name was used. This is still safer than just popping the top element
+ * off arbitrarily.
+ *
+ * @param tags the possible tags of the element to be ended
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder end(String... tags) {
+ checkDone();
+ try {
+ Element elem = stack.remove(0);
+ QName elemName = QName.of(elem);
+ boolean matched = false;
+ for (String tag : tags) {
+ if (elemName.equals(QName.parse(tag, namespaceBindings))) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) throw new IllegalStateException("element on top of stack is '" + elemName + "' not one of " + Arrays.asList(tags));
+ return this;
+ } catch (IndexOutOfBoundsException e) {
+ throw new IllegalStateException("no open elements to match end(" + Arrays.asList(tags) + ")");
+ }
+ }
+
+ /**
+ * Add an attribute to the currently open element. If an attribute with the same name was
+ * previously added to the element, overwrite its value.
+ *
+ * @param name the name of the attribute to add
+ * @param value the value of the attribute, will be converted to a string using {@link DataUtils#toXMLString(Object)}
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder attr(String name, Object value) {
+ checkDone();
+ if (current().getNodeType() != Node.ELEMENT_NODE) throw new IllegalStateException("current node is not an element");
+ QName.parse(name, namespaceBindings, null).setAttribute((Element) current(), DataUtils.toXMLString(value));
+ return this;
+ }
+
+ /**
+ * Add an attribute to the currently open element if the given condition holds. If condition
+ * is true, this behaves as {@link #attr(String, Object)}, otherwise it does nothing.
+ *
+ * @param condition the condition to satisfy before adding the attribute
+ * @param name the name of the attribute to add
+ * @param value the value of the attribute
+ * @return the element builder, for chaining calls
+ */
+ public ElementBuilder attrIf(boolean condition, String name, Object value) {
+ if (condition) attr(name, value);
+ return this;
+ }
+
+ /**
+ * Insert text into the currenly open element.
+ *
+ * @param text the text to insert
+ * @return this element builder, for chaining calls
+ */
+ public ElementBuilder text(Object text) {
+ checkDone();
+ appendText(DataUtils.toXMLString(text));
+ return this;
+ }
+
+ private void appendText(String text) {
+ Node textNode = doc.createTextNode(text);
+ if (stack.isEmpty()) {
+ if (!allowFragment) throw new IllegalStateException("unable to add a root text node in current context");
+ top.add(textNode);
+ } else {
+ if (current().getNodeType() != Node.ELEMENT_NODE) throw new IllegalStateException("current node is not an element");
+ current().appendChild(textNode);
+ }
+ }
+
+ /**
+ * Commit this element builder, persisting the recorded elements into the database.
+ *
+ * @return the newly created resource, as appropriate
+ */
+ public K commit() {
+ checkDone();
+ done = true;
+ if (stack.size() != 0) throw new IllegalStateException("can't commit with " + stack.size() + " elements left open");
+ if (top.isEmpty()) return null;
+ return callback.completed(top.toArray(new Node[top.size()]));
+ }
+
+ @Override
+ protected void finalize() {
+ if (!done) LOG.warn("disposed without commit");
+ }
+
+
+}
diff --git a/extensions/fluent/src/org/exist/fluent/Folder.java b/extensions/fluent/src/org/exist/fluent/Folder.java
index 914ee4e0584..6b092827ac0 100644
--- a/extensions/fluent/src/org/exist/fluent/Folder.java
+++ b/extensions/fluent/src/org/exist/fluent/Folder.java
@@ -1,1213 +1,1213 @@
-package org.exist.fluent;
-
-import java.io.*;
-import java.util.*;
-
-import org.exist.EXistException;
-import org.exist.collections.*;
-import org.exist.collections.Collection;
-import org.exist.collections.triggers.TriggerException;
-import org.exist.dom.*;
-import org.exist.security.PermissionDeniedException;
-import org.exist.storage.DBBroker;
-import org.exist.storage.lock.Lock;
-import org.exist.util.LockException;
-import org.exist.xmldb.XmldbURI;
-import org.exist.xquery.XPathException;
-import org.exist.xquery.value.*;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
-
-
-/**
- *
A named collection of XML documents in the database. Each document belongs
- * to precisely one folder. Folders can be nested, with queries carried out
- * within either the scope of a single folder or of a whole subtree.
- *
- *
Though the name Collection is commonly used for these
- * constructs in the context of XML databases, the name Folder was
- * chosen instead to avoid conflicting with the ubiquitous Java java.util.Collection
- * interface.
- *
- *
Instances of this class are not thread-safe.
- *
- * @author Piotr Kaminski
- * @version $Revision: 1.31 $ ($Date: 2006/09/04 06:09:05 $)
- */
-public class Folder extends NamedResource implements Cloneable {
-
- /**
- * Listener for events affecting folders. The three possible actions are folder
- * creation, renaming (update), and deletion.
- *
- * WARNING: as of September 1, 2005, eXist does not implement
- * folder triggers so folder listeners are ineffective. This warning will be removed
- * when the situation is known to have been corrected.
- *
- * @author Piotr Kaminski
- */
- public interface Listener extends org.exist.fluent.Listener {
- /**
- * Respond to a folder event.
- *
- * @param ev the details of the event
- */
- void handle(Folder.Event ev);
- }
-
- /**
- * An event that concerns a folder.
- *
- * @author Piotr Kaminski
- */
- public static class Event extends org.exist.fluent.Listener.Event {
- /**
- * The folder that's the subject of this event.
- * Note that for some timing/action combinations, this field might be null.
- */
- public final Folder folder;
-
- Event(Trigger trigger, String path, Folder folder) {
- super(trigger, path);
- this.folder = folder;
- }
-
- Event(ListenerManager.EventKey key, Folder folder) {
- super(key);
- this.folder = folder;
- }
-
- @Override
- public boolean equals(Object o) {
- return
- super.equals(o) &&
- (folder == null ? ((Event) o).folder == null : folder.equals(((Event) o).folder));
- }
-
- @Override
- public int hashCode() {
- return super.hashCode() * 37 + (folder == null ? 0 : folder.hashCode());
- }
-
- @Override
- public String toString() {
- StringBuilder buf = new StringBuilder(super.toString());
- buf.insert(3, "Folder.");
- buf.insert(buf.length()-1, ", " + folder);
- return buf.toString();
- }
- }
-
- /**
- * The children (subfolders) facet of a collection. Permits operations on subfolders,
- * both immediate and indirect.
- *
- * @author Piotr Kaminski
- */
- public class ChildrenFacet implements Iterable {
-
- private ChildrenFacet() {}
-
- /**
- * Return an existing descendant this of this folder, inheriting this one's namespace mappings.
- *
- * @param descendantName the relative name of the child folder to get; must not start with '/'
- * @return the descendant folder with the given name
- */
- public Folder get(String descendantName) {
- staleMarker.check();
- if (descendantName.startsWith("/")) throw new IllegalArgumentException("descendant name starts with '/': " + descendantName);
- String parentPath = path();
- if (parentPath.equals("/")) parentPath = "";
- return new Folder(parentPath + "/" + descendantName, false, Folder.this);
- }
-
- /**
- * Get a descendant folder of this folder, or create it if it doesn't exist. It inherits
- * this folder's namespace mappings.
- *
- * @param descendantName the relative name of the descendant folder to create; must not start with '/'
- * @return the child folder with the given name
- */
- public Folder create(String descendantName) {
- staleMarker.check();
- if (descendantName.startsWith("/")) throw new IllegalArgumentException("descendant name starts with '/': " + descendantName);
- String parentPath = path();
- if (parentPath.equals("/")) parentPath = "";
- return new Folder(parentPath + "/" + descendantName, true, Folder.this);
- }
-
- /**
- * Return the number of immediate child folders of this folder.
- *
- * @return the number of child subfolders
- */
- public int size() {
- DBBroker _broker = null;
- try {
- _broker = db.acquireBroker();
- return getQuickHandle().getChildCollectionCount(_broker);
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- db.releaseBroker(_broker);
- }
-
- }
-
- /**
- * Return whether this folder has a descendant folder with the given name.
- *
- * @param descendantName the relative name of the descendant folder; must not start with '/'
- * @return true if this folder has a descendant wich the given name, false otherwise
- */
- public boolean contains(String descendantName) {
- staleMarker.check();
- if (descendantName.startsWith("/")) throw new IllegalArgumentException("child name starts with '/': " + descendantName);
- DBBroker _broker = null;
- try {
- _broker = db.acquireBroker();
- return _broker.getCollection(XmldbURI.create(path() + "/" + descendantName)) != null;
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- db.releaseBroker(_broker);
- }
- }
-
- /**
- * Export these children folders to the given directory.
- * If the directory does not exist it will be created. A subdirectory will be created
- * for each child folder.
- *
- * @param destination the destination folder to export into
- * @throws IOException if the export failed due to an I/O error
- */
- public void export(File destination) throws IOException {
- if (!destination.exists()) destination.mkdirs();
- if (!destination.isDirectory()) throw new IOException("export destination not a directory: " + destination);
- for (Folder child : this) {
- child.export(new File(destination, child.name()));
- }
- }
-
- public class FolderIterator implements Iterator {
- private Folder last;
-
- private Iterator delegate = null;
- private DBBroker broker = null;
-
- private Iterator getDelegate() throws IllegalStateException {
- if(delegate == null) {
- try {
- broker = db.acquireBroker();
- delegate = getQuickHandle().collectionIterator(broker);
- } catch(PermissionDeniedException pde) {
- throw new IllegalStateException(pde.getMessage(), pde);
- }
- }
-
- return delegate;
- }
-
- @Override
- public void remove() {
- staleMarker.check();
- if (last == null) {
- throw new IllegalStateException("no collection to remove");
- }
- last.delete();
- last = null;
- }
-
- @Override
- public boolean hasNext() {
- staleMarker.check();
- boolean result = getDelegate().hasNext();
- if(!result) {
- db.releaseBroker(broker);
- }
- return result;
- }
-
- @Override
- public Folder next() {
- staleMarker.check();
- last = get(getDelegate().next().getCollectionPath());
- return last;
- }
- }
-
- /**
- * Return an iterator over the immediate child subfolders. You can use this iterator to
- * selectively delete subfolders as well.
- *
- * @return an iterator over the child subfolders
- */
- @Override
- public Iterator iterator() {
- staleMarker.check();
- return new FolderIterator();
- }
-
- }
-
- /**
- * The immediate documents facet of a folder. Gives access to the (conceptual) set of
- * documents contained directly in a folder. All functions will not consider
- * documents contained in subfolders.
- *
- * @author Piotr Kaminski
- */
- public class DocumentsFacet extends Resource implements Iterable {
-
- private ListenersFacet listeners;
-
- /**
- * The facet that gives control over listeners for documents contained directly within
- * a folder.
- *
- * @author Piotr Kaminski
- */
- public class ListenersFacet {
-
- /**
- * Add a listener for all documents directly in this folder. Equivalent to add(EnumSet.of(trigger), listener).
- *
- * @see #add(Set, Document.Listener)
- * @param trigger the kind of event the listener should be notified of
- * @param listener the listener to notify of events
- */
- public void add(Trigger trigger, Document.Listener listener) {
- add(EnumSet.of(trigger), listener);
- }
-
- /**
- * Add a listener for all documents directly in this folder.
- *
- * @param triggers the kinds of events the listener should be notified of; the set must not be empty
- * @param listener the listener to notify of events
- */
- public void add(Set triggers, Document.Listener listener) {
- staleMarker.check();
- ListenerManager.INSTANCE.add(path(), ListenerManager.Depth.ONE, triggers, listener, Folder.this);
- }
-
- /**
- * Remove a listener previously added through this facet. This will remove the listener from
- * all combinations of timing and action for this folder, even if added via multiple invocations
- * of the add methods. However, it will not remove the listener from combinations
- * added through other facets.
- *
- * @param listener the listener to remove
- */
- public void remove(Document.Listener listener) {
- ListenerManager.INSTANCE.remove(path(), ListenerManager.Depth.ONE, listener);
- }
- }
-
- private DocumentsFacet() {
- super(Folder.this.namespaceBindings, Folder.this.db);
- }
-
- @Override Sequence convertToSequence() {
- staleMarker.check();
- return getDocsSequence(false);
- }
-
- /**
- * Return the facet that allows control over listenersof the folder's immediate documents.
- *
- * @return the immediate documents' listener facet
- */
- public ListenersFacet listeners() {
- if (listeners == null) listeners = new ListenersFacet();
- return listeners;
- }
-
- /**
- * Build a new XML document in this collection, with the given relative path. Remember to {@link ElementBuilder#commit commit}
- * commit the builder when done. If the builder doesn't commit, no document is created.
- *
- * @param name the relative path to the new document
- * @return a builder to use to create the document
- */
- public ElementBuilder build(final Name name) {
- Folder target = name.stripPathPrefix(Folder.this);
- if (target != Folder.this) return target.documents().build(name);
-
- staleMarker.check();
- return new ElementBuilder(Folder.this.namespaceBindings(), false, new ElementBuilder.CompletedCallback() {
- public XMLDocument completed(Node[] nodes) {
- assert nodes.length == 1;
- Node node = nodes[0];
- transact(Lock.WRITE_LOCK);
- try {
- name.setContext(handle);
- IndexInfo info = handle.validateXMLResource(tx.tx, broker, XmldbURI.create(name.get()), node);
- changeLock(Lock.NO_LOCK);
- handle.store(tx.tx, broker, info, node, false);
- commit();
- } catch (EXistException e) {
- throw new DatabaseException(e);
- } catch (PermissionDeniedException e) {
- throw new DatabaseException(e);
- } catch (TriggerException e) {
- throw new DatabaseException(e);
- } catch (SAXException e) {
- throw new DatabaseException(e);
- } catch (LockException e) {
- throw new DatabaseException(e);
- } catch (IOException e) {
- throw new DatabaseException(e);
- } finally {
- release();
- }
- return get(name.get()).xml();
- }
- });
- }
-
- /**
- * Return whether this facet's folder contains a document with the given relative path.
- *
- * @param documentPath the relative path of the document to check for
- * @return true if the folder contains a document with the given path, false otherwise
- */
- public boolean contains(String documentPath) {
- checkIsRelativeDocPath(documentPath);
- if (documentPath.contains("/")) return database().contains(Folder.this.path() + "/" + documentPath);
-
- DBBroker _broker = null;
- try {
- _broker = db.acquireBroker();
- return getQuickHandle().getDocument(_broker, XmldbURI.create(documentPath)) != null;
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- if(_broker != null) {
- db.releaseBroker(_broker);
- }
- }
-
- }
-
- /**
- * Create an XML document with the given relative path, takings its contents from the given source.
- *
- * @param name the desired relative path of the document
- * @param source the source of XML data to read in the document contents from; the folder's namespace bindings are not applied
- * @return the newly created document
- * @throws DatabaseException if anything else goes wrong
- */
- public XMLDocument load(Name name, Source.XML source) {
- Folder target = name.stripPathPrefix(Folder.this);
- if (target != Folder.this) return target.documents().load(name, source);
-
- transact(Lock.WRITE_LOCK);
- try {
- source.applyOldName(name);
- name.setContext(handle);
- IndexInfo info = handle.validateXMLResource(tx.tx, broker, XmldbURI.create(name.get()), source.toInputSource());
- changeLock(Lock.NO_LOCK);
- handle.store(tx.tx, broker, info, source.toInputSource(), false);
- commit();
- } catch (EXistException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (PermissionDeniedException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (TriggerException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (SAXException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (LockException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (IOException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } finally {
- release();
- }
- return get(name.get()).xml();
- }
-
- /**
- * Create a binary document with the given relative path, takings its contents from the given source.
- *
- * @param name the desired relative path of the document
- * @param source the source to read the document contents from
- * @return the newly created document
- * @throws DatabaseException if anything else goes wrong
- */
- public Document load(Name name, Source.Blob source) {
- Folder target = name.stripPathPrefix(Folder.this);
- if (target != Folder.this) return target.documents().load(name, source);
-
- transact(Lock.WRITE_LOCK);
- try {
- source.applyOldName(name);
- name.setContext(handle);
- InputStream inputStream = source.toInputStream();
- try {
- handle.addBinaryResource(tx.tx, broker, XmldbURI.create(name.get()), inputStream, null, source.getLength());
- } catch (PermissionDeniedException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (LockException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (TriggerException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } catch (EXistException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } finally {
- inputStream.close();
- }
- commit();
- } catch (IOException e) {
- throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
- } finally {
- release();
- }
- return get(name.get());
- }
-
- /**
- * Get the contained document with the given relative path.
- *
- * @param documentPath the relative path of the document to find
- * @return the document with the given path
- * @throws DatabaseException if unable to find or access the desired document
- */
- public Document get(String documentPath) {
- checkIsRelativeDocPath(documentPath);
- if (documentPath.contains("/")) return database().getDocument(Folder.this.path() + "/" + documentPath);
-
- DBBroker _broker = null;
- try {
- _broker = db.acquireBroker();
- DocumentImpl dimpl = getQuickHandle().getDocument(_broker, XmldbURI.create(documentPath));
- if (dimpl == null) throw new DatabaseException("no such document: " + documentPath);
- return Document.newInstance(dimpl, Folder.this);
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- if(_broker != null) {
- db.releaseBroker(_broker);
- }
- }
- }
-
- private void checkIsRelativeDocPath(String docPath) {
- if (docPath.startsWith("/")) throw new IllegalArgumentException("relative path cannot start with '/': " + docPath);
- if (docPath.endsWith("/")) throw new IllegalArgumentException("relative path cannot end with '/': " + docPath);
- }
-
- /**
- * Return the number of documents immediately contained in the folder.
- *
- * @return the number of child documents
- */
- public int size() {
- DBBroker _broker = null;
- try {
- _broker = db.acquireBroker();
- return getQuickHandle().getDocumentCount(_broker);
- } catch (PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- db.releaseBroker(_broker);
- }
- }
-
- /**
- * Export the immediately contained documents to the given directory.
- * If the directory does not exist it will be created. Documents in this
- * folder will appear directly in the given directory.
- *
- * @param destination the destination folder to export into
- * @throws IOException if the export failed due to an I/O error
- */
- public void export(File destination) throws IOException {
- if (!destination.exists()) destination.mkdirs();
- if (!destination.isDirectory()) throw new IOException("export destination not a directory: " + destination);
- for (Document doc : this) {
- doc.export(new File(destination, doc.name()));
- }
- }
-
- /**
- * Query over the documents immediately contained in the folder, ignoring any documents
- * in subfolders.
- *
- * @return a query service over the folder's immediate documents
- */
- @Override public QueryService query() {
- staleMarker.check();
- return super.query();
- }
-
- @Override QueryService createQueryService() {
- return new QueryService(Folder.this) {
- @Override
- protected void prepareContext(DBBroker broker_) {
- acquire(Lock.READ_LOCK, broker_);
- try {
- docs = handle.allDocs(broker_, new DefaultDocumentSet(), false);
- baseUri = new AnyURIValue(handle.getURI());
- }catch (PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- release();
- }
- }
- };
- }
-
- /**
- * Return an iterator over the folder's immediate documents. This iterator can be used
- * to selectively delete documents as well.
- *
- * @return an iterator over the folder's immediate documents
- */
- public Iterator iterator() {
- return new Iterator() {
- private Iterator delegate;
- private Document last;
- {
- acquire(Lock.READ_LOCK);
- try {
- delegate = handle.iterator(broker);
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- release();
- }
- }
- public void remove() {
- staleMarker.check();
- if (last == null) throw new IllegalStateException("no document to remove");
- last.delete();
- last = null;
- }
- public boolean hasNext() {
- staleMarker.check();
- return delegate.hasNext();
- }
- public Document next() {
- staleMarker.check();
- last = Document.newInstance(delegate.next(), Folder.this);
- return last;
- }
- };
- }
-
- }
-
- /**
- * The facet that allwos control over listeners to the folder, and all its descendant folders and
- * documents.
- *
- * @author Piotr Kaminski
- */
- public class ListenersFacet {
-
- /**
- * Add a listener for either folder or document events on this folder, its contents, and all
- * its descendants and their contents as well. Equivalent to add(EnumSet.of(trigger), listener).
- *
- * @see #add(Set, org.exist.fluent.Listener)
- * @param trigger the kind of event the listener should be notified of
- * @param listener the listener to notify of events
- */
- public void add(Trigger trigger, org.exist.fluent.Listener listener) {
- add(EnumSet.of(trigger), listener);
- }
-
- /**
- * Add a listener for either folder or document events on this folder, its contents, and all
- * its descendants and their contents as well. The listener's type (either {@link Document.Listener}
- * or {@link Folder.Listener}) will determine the kinds of events it will receive. Note that
- * if the listener implements both interfaces it will be notified of events that concern both
- * documents and folders (and satisfy the timing and action characteristics requested).
- *
- * @param triggers the kinds of events the listener should be notified of; the set must not be empty
- * @param listener the listener to notify of events
- */
- public void add(Set triggers, org.exist.fluent.Listener listener) {
- staleMarker.check();
- ListenerManager.INSTANCE.add(path(), ListenerManager.Depth.MANY, triggers, listener, Folder.this);
- }
-
- /**
- * Remove a listener previously added through this facet. This will remove the listener from
- * all combinations of timing and action for this folder, even if added via multiple invocations
- * of the add methods. However, it will not remove the listener from combinations
- * added through other facets, even this folder's documents facet or the facets of any descendants.
- *
- * @param listener the listener to remove
- */
- public void remove(org.exist.fluent.Listener listener) {
- ListenerManager.INSTANCE.remove(path(), ListenerManager.Depth.MANY, listener);
- }
- }
-
-
-
- private String path;
- private StaleMarker staleMarker;
- private ChildrenFacet children;
- private DocumentsFacet documents;
- private ListenersFacet listeners;
- private MetadataFacet metadata;
-
- // the following are only valid while we're holding a broker
- private DBBroker broker;
- private Collection handle;
- private Transaction tx;
- private int lockMode;
- private boolean ownBroker;
-
- /**
- * Create a wrapper around the given collection.
- *
- * @param path the absolute path to the desired collection, must start with '/'
- * @param createIfMissing what to do if the given collection doesn't exist; if true, create it, otherwise signal an error
- * @param origin the origin, indicating the database instance and namespace bindings to be extended
- * @throws DatabaseException if the collection cannot be found and is not supposed to be created
- */
- Folder(String path, boolean createIfMissing, Resource origin) {
- this(path, createIfMissing, origin.namespaceBindings().extend(), origin.database());
- }
-
- Folder(String path, boolean createIfMissing, NamespaceMap namespaceBindings, Database db) {
- super(namespaceBindings, db);
- if (path.length() == 0 || path.charAt(0) != '/') throw new IllegalArgumentException("path must start with /, got " + path);
-
- try {
- broker = db.acquireBroker();
- Collection collection;
- if (createIfMissing) {
- tx = Database.requireTransaction();
- try {
- collection = createInternal(path);
- tx.commit();
- } finally {
- tx.abortIfIncomplete();
- }
- } else {
- try {
- collection = broker.getCollection(XmldbURI.create(path));
-
- if (collection == null) {
- throw new DatabaseException("collection not found '" + path + "'");
- }
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- }
- }
- // store the normalized path, minus the root prefix if possible
- changePath(collection.getURI().getCollectionPath());
- } finally {
- db.releaseBroker(broker);
- broker = null;
- tx = null;
- }
- }
-
- private void changePath(String newPath) {
- this.path = Database.normalizePath(newPath);
- staleMarker = new StaleMarker();
- staleMarker.track(path);
- }
-
- /**
- * Return this folder's listeners facet, giving control over listeners to events on this folder,
- * its contents, and all its descendants.
- *
- * @return this folder's listeners facet
- */
- public ListenersFacet listeners() {
- if (listeners == null) listeners = new ListenersFacet();
- return listeners;
- }
-
- @Override public MetadataFacet metadata() {
- if (metadata == null) metadata = new MetadataFacet(getQuickHandle().getPermissionsNoLock(), db) {
- @Override public Date creationDate() {
- return new Date(getQuickHandle().getCreationTime());
- }
- };
- return metadata;
- }
-
- /**
- * Create a duplicate handle that copies the original's path and namespace bindings.
- * No copy is created of the underlying folder. The namespace bindings will copy the
- * original's immediate namespace map and namespace bindings inheritance chain.
- *
- * @return a duplicate of this collection wrapper
- */
- @Override
- public Folder clone() {
- staleMarker.check();
- return new Folder(path(), false, namespaceBindings.clone(), db);
- }
-
- public Folder cloneWithoutNamespaceBindings() {
- staleMarker.check();
- return new Folder(path(), false, new NamespaceMap(), db);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Folder) return path().equals(((Folder) o).path());
- return false;
- }
-
- @Override
- public int hashCode() {
- return path().hashCode();
- }
-
- @Override
- public String toString() {
- return "folder '" + path() + "'";
- }
-
- private Collection createInternal(String targetPath) {
- try {
- Collection collection = broker.getOrCreateCollection(tx.tx, XmldbURI.create(targetPath));
- broker.saveCollection(tx.tx, collection);
- broker.flush();
- return collection;
- } catch (PermissionDeniedException e) {
- throw new DatabaseException(e);
- } catch (IOException e) {
- throw new DatabaseException(e);
- } catch (TriggerException e) {
- throw new DatabaseException(e);
- }
- }
-
- void transact(int _lockMode) {
- if (tx != null) throw new IllegalStateException("transaction already in progress");
- tx = Database.requireTransaction();
- acquire(_lockMode);
- }
-
- void commit() {
- if (tx == null) throw new IllegalStateException("no transaction in progress");
- tx.commit(); // later aborts will do nothing
- }
-
- void acquire(int _lockMode) {
- DBBroker _broker = db.acquireBroker();
- ownBroker = true;
- try {
- acquire(_lockMode, _broker);
- } catch (RuntimeException e) {
- db.releaseBroker(_broker);
- ownBroker = false;
- throw e;
- }
- }
-
- void acquire(int _lockMode, DBBroker _broker) {
- staleMarker.check();
- if(broker != null || handle != null) throw new IllegalStateException("broker already acquired");
- broker = _broker;
- try {
- handle = broker.openCollection(XmldbURI.create(path()), _lockMode);
- if(handle == null) {
- throw new DatabaseException("collection not found '" + path + "'");
- }
- this.lockMode = _lockMode;
- } catch (RuntimeException e) {
- broker = null;
- handle = null;
- throw e;
- } catch(PermissionDeniedException pde) {
- broker = null;
- handle = null;
- throw new DatabaseException(pde.getMessage(), pde);
- }
- }
-
- void release() {
- if (broker == null || handle == null) throw new IllegalStateException("broker not acquired");
- if (tx != null) tx.abortIfIncomplete();
- if (lockMode != Lock.NO_LOCK) handle.getLock().release(lockMode);
- if (ownBroker) db.releaseBroker(broker);
- ownBroker = false;
- broker = null;
- handle = null;
- tx = null;
- }
-
- void changeLock(int newLockMode) {
- if (broker == null || handle == null) throw new IllegalStateException("broker not acquired");
- if (lockMode == newLockMode) return;
- if (lockMode == Lock.NO_LOCK) {
- try {
- handle.getLock().acquire(newLockMode);
- lockMode = newLockMode;
- } catch (LockException e) {
- throw new DatabaseException(e);
- }
- } else {
- if (newLockMode != Lock.NO_LOCK) throw new IllegalStateException("cannot change between read and write lock modes");
- handle.getLock().release(lockMode);
- lockMode = newLockMode;
- }
- }
-
- private Collection getQuickHandle() {
- acquire(Lock.NO_LOCK);
- try {
- return handle;
- } finally {
- release();
- }
- }
-
- /**
- * Return whether this folder is empty, i.e. has no documents or subfolders in it.
- *
- * @return whether this folder is empty
- */
- public boolean isEmpty() {
- acquire(Lock.NO_LOCK);
- try {
- return handle.getDocumentCount(broker) == 0 && handle.getChildCollectionCount(broker) == 0;
- } catch (PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- release();
- }
-
- }
-
- /**
- * Remove all resources and subfolders from this folder, but keep the folder itself.
- */
- public void clear() {
- transact(Lock.READ_LOCK);
- try {
- if (handle.getDocumentCount(broker) == 0 && handle.getChildCollectionCount(broker) == 0) return;
- broker.removeCollection(tx.tx, handle);
- createInternal(path);
- commit();
- } catch (PermissionDeniedException e) {
- throw new DatabaseException(e);
- } catch (IOException e) {
- throw new DatabaseException(e);
- } catch (TriggerException e) {
- throw new DatabaseException(e);
- } finally {
- release();
- }
- changePath(path); // reset stale marker
- }
-
- /**
- * Delete this folder, including all documents and descendants. If invoked on the root folder,
- * deletes all documents and descendants but does not delete the root folder itself.
- */
- @Override public void delete() {
- transact(Lock.NO_LOCK);
- try {
- // TODO: temporary hack, remove once removing root collection works in eXist
- if (path.equals("/")) {
- for (Iterator it = handle.iterator(broker); it.hasNext();) {
- DocumentImpl dimpl = it.next();
- if (dimpl instanceof BinaryDocument) {
- handle.removeBinaryResource(tx.tx, broker, dimpl);
- } else {
- handle.removeXMLResource(tx.tx, broker, dimpl.getFileURI());
- }
- }
- }
- // end hack
- broker.removeCollection(tx.tx, handle);
- commit();
- } catch (PermissionDeniedException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new DatabaseException(e);
- } catch (LockException e) {
- throw new DatabaseException(e);
- } catch (TriggerException e) {
- throw new DatabaseException(e);
- } finally {
- release();
- }
- }
-
- /**
- * Move this folder to another spot in the folder hierarchy, possibly changing its name in the process.
- * This folder will refer to the newly relocated folder when this method returns. You can use this
- * method to move a folder without changing its name (folder.move(destFolder, Name.keepCreate()))
- * or to rename it without changing its location (folder.move(folder.parent(), Name.create(newName))).
- *
- * @param destination the new parent folder of this folder
- * @param name the new name for this folder
- */
- @Override public void move(Folder destination, Name name) {
- changePath(moveOrCopyThisFolder(destination, name, false));
- }
-
- /**
- * Copy this folder to another place in the folder hierarchy, returning the newly copied folder
- * with namespace bindings inherited from this one.
- *
- * @param destination the desired parent folder of the copy
- * @param name the desired name of the copy
- * @return a reference to the copied folder
- */
- @Override public Folder copy(Folder destination, Name name) {
- return new Folder(moveOrCopyThisFolder(destination, name, true), false, this);
- }
-
- private String moveOrCopyThisFolder(Folder destination, Name name, boolean copy) {
- db.checkSame(destination);
- transact(Lock.WRITE_LOCK);
- try {
- destination.acquire(Lock.WRITE_LOCK, broker);
- try {
- name.setOldName(name());
- name.setContext(handle);
- if (copy) {
- broker.copyCollection(tx.tx, handle, destination.handle, XmldbURI.create(name.get()));
- } else {
- broker.moveCollection(tx.tx, handle, destination.handle, XmldbURI.create(name.get()));
- }
- commit();
- } catch (PermissionDeniedException e) {
- throw new DatabaseException(e);
- } catch (LockException e) {
- throw new DatabaseException(e);
- } catch (IOException e) {
- throw new DatabaseException(e);
- } catch (TriggerException e) {
- throw new DatabaseException(e);
- } catch (EXistException e) {
- throw new DatabaseException(e);
- } finally {
- destination.release();
- }
- } finally {
- release();
- }
- return destination.path() + "/" + name.get();
- }
-
- /**
- * Provide access to the documents facet of this folder. The documents facet is
- * the conceptual set of documents contained directly in this collection (and therefore
- * excludes documents contained in any subfolders).
- *
- * @return the documents facet
- */
- public DocumentsFacet documents() {
- if (documents == null) documents = new DocumentsFacet();
- return documents;
- }
-
- /**
- * Return whether this folder or one of its descendants contains the given named resource.
- *
- * @param res the resource to check for
- * @return true if the resource is contained (directly or indirectly) in this folder, false otherwise
- */
- public boolean contains(NamedResource res) {
- staleMarker.check();
- db.checkSame(res);
- return res.path().startsWith(path() + (path().equals("/") ? "" : "/"));
- }
-
- /**
- * Return the given path stripped of this folder's prefix path, i.e. relative to this folder.
- * The given path can be either to a document or to a subfolder. If the given path is to
- * this folder itself, return the empty string.
- *
- * @param subPath the path to relativize
- * @return the given path relative to this folder
- */
- public String relativePath(String subPath) {
- if (subPath.equals(path())) return "";
- if (path().equals("/") && subPath.charAt(0) == '/') return subPath.substring(1);
- if (!subPath.startsWith(path()+"/")) throw new IllegalArgumentException("path '" + subPath + "' does not fall under this collection's path '" + path() + "'");
- return subPath.substring(path().length() + 1);
- }
-
- /**
- * Return the full path to the folder; always starts with '/'.
- *
- * @return the full path to the folder
- */
- @Override public String path() {
- return path;
- }
-
- /**
- * Return the local name of the folder; this is the last segment of its path.
- *
- * @return the local name of the folder
- */
- @Override public String name() {
- return path().substring(path().lastIndexOf('/')+1);
- }
-
- /**
- * Return the parent folder of this folder. It will inherit this folder's namespace bindings.
- *
- * @return the parent folder that this folder is a child of
- * @throws DatabaseException if this is the root folder
- */
- public Folder parent() {
- String parentPath = getQuickHandle().getURI().removeLastSegment().getCollectionPath();
- if (parentPath == null || parentPath.length() == 0) throw new DatabaseException("this is the root collection");
- return new Folder(parentPath, false, this);
- }
-
- /**
- * Return the children facet of this folder that gives access to operations on its subfolders
- * and descendants.
- *
- * @return the children facet of this folder
- */
- public ChildrenFacet children() {
- if (children == null) children = new ChildrenFacet();
- return children;
- }
-
- /**
- * Export the contents of this folder (both documents and subfolders) to the given
- * directory. If the directory does not exist it will be created. Documents in this
- * folder will appear directly in the given directory, i.e. a subdirectory matching this
- * folder will not be created.
- *
- * @param destination the destination folder to export into
- * @throws IOException if the export failed due to an I/O error
- */
- public void export(File destination) throws IOException {
- documents().export(destination);
- children().export(destination);
- }
-
- @Override Sequence convertToSequence() {
- staleMarker.check();
- return getDocsSequence(true);
- }
-
- private Sequence getDocsSequence(boolean recursive) {
- try {
- DocumentSet docs;
- acquire(Lock.READ_LOCK);
- try {
- docs = handle.allDocs(broker, new DefaultDocumentSet(), recursive);
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- release();
- }
- Sequence result = new ExtArrayNodeSet(docs.getDocumentCount(), 1);
- for (Iterator> i = docs.getDocumentIterator(); i.hasNext();) {
- result.add(new NodeProxy((DocumentImpl) i.next()));
- }
- return result;
- } catch (XPathException e) {
- throw new RuntimeException("unexpected exception converting document set to sequence", e);
- }
- }
-
- /**
- * Return a query service for executing queries over the contents of this folder and the contents
- * of all its descendants. If you only want to query the documents contained directly in this folder,
- * see {@link #documents() the documents facet}.
- *
- * @return a query service over this folder's contents and all its descendants' contents
- */
- @Override public QueryService query() {
- staleMarker.check();
- return super.query();
- }
-
- @Override QueryService createQueryService() {
- return new QueryService(this) {
- @Override void prepareContext(DBBroker broker_) {
- acquire(Lock.READ_LOCK, broker_);
- try {
- docs = handle.allDocs(broker_, new DefaultDocumentSet(), true);
- baseUri = new AnyURIValue(handle.getURI());
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- release();
- }
- }
- };
- }
-
- void removeDocument(DocumentImpl dimpl) {
- transact(Lock.WRITE_LOCK);
- try {
- if (dimpl instanceof BinaryDocument) {
- handle.removeBinaryResource(tx.tx, broker, dimpl.getFileURI());
- } else {
- handle.removeXMLResource(tx.tx, broker, dimpl.getFileURI());
- }
- commit();
- } catch (PermissionDeniedException e) {
- throw new DatabaseException(e);
- } catch (LockException e) {
- throw new DatabaseException(e);
- } catch (TriggerException e) {
- throw new DatabaseException(e);
- } finally {
- release();
- }
- }
-
- DocumentImpl moveOrCopyDocument(DocumentImpl doc, Name name, boolean copy) {
- XmldbURI uri;
- transact(Lock.WRITE_LOCK);
- try {
- name.setContext(handle);
- uri = XmldbURI.create(name.get());
- if (copy) {
- tx.lockRead(doc);
- broker.copyResource(tx.tx, doc, handle, uri);
- } else {
- tx.lockWrite(doc);
- broker.moveResource(tx.tx, doc, handle, uri);
- }
- commit();
- } catch (PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } catch (LockException e) {
- throw new DatabaseException("lock denied", e);
- } catch (IOException e) {
- throw new DatabaseException(e);
- } catch (TriggerException e) {
- throw new DatabaseException(e);
- } catch (EXistException e) {
- throw new DatabaseException(e);
- } finally {
- release();
- }
-
- DBBroker _broker = null;
- Collection _handle = getQuickHandle();
- try {
- _broker = db.acquireBroker();
- return _handle.getDocument(_broker, uri);
- } catch(PermissionDeniedException pde) {
- throw new DatabaseException(pde.getMessage(), pde);
- } finally {
- if(_broker != null) {
- db.releaseBroker(_broker);
- }
- }
-
-
- }
+package org.exist.fluent;
+
+import java.io.*;
+import java.util.*;
+
+import org.exist.EXistException;
+import org.exist.collections.*;
+import org.exist.collections.Collection;
+import org.exist.collections.triggers.TriggerException;
+import org.exist.dom.*;
+import org.exist.security.PermissionDeniedException;
+import org.exist.storage.DBBroker;
+import org.exist.storage.lock.Lock;
+import org.exist.util.LockException;
+import org.exist.xmldb.XmldbURI;
+import org.exist.xquery.XPathException;
+import org.exist.xquery.value.*;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+
+/**
+ *
A named collection of XML documents in the database. Each document belongs
+ * to precisely one folder. Folders can be nested, with queries carried out
+ * within either the scope of a single folder or of a whole subtree.
+ *
+ *
Though the name Collection is commonly used for these
+ * constructs in the context of XML databases, the name Folder was
+ * chosen instead to avoid conflicting with the ubiquitous Java java.util.Collection
+ * interface.
+ *
+ *
Instances of this class are not thread-safe.
+ *
+ * @author Piotr Kaminski
+ * @version $Revision: 1.31 $ ($Date: 2006/09/04 06:09:05 $)
+ */
+public class Folder extends NamedResource implements Cloneable {
+
+ /**
+ * Listener for events affecting folders. The three possible actions are folder
+ * creation, renaming (update), and deletion.
+ *
+ * WARNING: as of September 1, 2005, eXist does not implement
+ * folder triggers so folder listeners are ineffective. This warning will be removed
+ * when the situation is known to have been corrected.
+ *
+ * @author Piotr Kaminski
+ */
+ public interface Listener extends org.exist.fluent.Listener {
+ /**
+ * Respond to a folder event.
+ *
+ * @param ev the details of the event
+ */
+ void handle(Folder.Event ev);
+ }
+
+ /**
+ * An event that concerns a folder.
+ *
+ * @author Piotr Kaminski
+ */
+ public static class Event extends org.exist.fluent.Listener.Event {
+ /**
+ * The folder that's the subject of this event.
+ * Note that for some timing/action combinations, this field might be null.
+ */
+ public final Folder folder;
+
+ Event(Trigger trigger, String path, Folder folder) {
+ super(trigger, path);
+ this.folder = folder;
+ }
+
+ Event(ListenerManager.EventKey key, Folder folder) {
+ super(key);
+ this.folder = folder;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return
+ super.equals(o) &&
+ (folder == null ? ((Event) o).folder == null : folder.equals(((Event) o).folder));
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() * 37 + (folder == null ? 0 : folder.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buf = new StringBuilder(super.toString());
+ buf.insert(3, "Folder.");
+ buf.insert(buf.length()-1, ", " + folder);
+ return buf.toString();
+ }
+ }
+
+ /**
+ * The children (subfolders) facet of a collection. Permits operations on subfolders,
+ * both immediate and indirect.
+ *
+ * @author Piotr Kaminski
+ */
+ public class ChildrenFacet implements Iterable {
+
+ private ChildrenFacet() {}
+
+ /**
+ * Return an existing descendant this of this folder, inheriting this one's namespace mappings.
+ *
+ * @param descendantName the relative name of the child folder to get; must not start with '/'
+ * @return the descendant folder with the given name
+ */
+ public Folder get(String descendantName) {
+ staleMarker.check();
+ if (descendantName.startsWith("/")) throw new IllegalArgumentException("descendant name starts with '/': " + descendantName);
+ String parentPath = path();
+ if (parentPath.equals("/")) parentPath = "";
+ return new Folder(parentPath + "/" + descendantName, false, Folder.this);
+ }
+
+ /**
+ * Get a descendant folder of this folder, or create it if it doesn't exist. It inherits
+ * this folder's namespace mappings.
+ *
+ * @param descendantName the relative name of the descendant folder to create; must not start with '/'
+ * @return the child folder with the given name
+ */
+ public Folder create(String descendantName) {
+ staleMarker.check();
+ if (descendantName.startsWith("/")) throw new IllegalArgumentException("descendant name starts with '/': " + descendantName);
+ String parentPath = path();
+ if (parentPath.equals("/")) parentPath = "";
+ return new Folder(parentPath + "/" + descendantName, true, Folder.this);
+ }
+
+ /**
+ * Return the number of immediate child folders of this folder.
+ *
+ * @return the number of child subfolders
+ */
+ public int size() {
+ DBBroker _broker = null;
+ try {
+ _broker = db.acquireBroker();
+ return getQuickHandle().getChildCollectionCount(_broker);
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ db.releaseBroker(_broker);
+ }
+
+ }
+
+ /**
+ * Return whether this folder has a descendant folder with the given name.
+ *
+ * @param descendantName the relative name of the descendant folder; must not start with '/'
+ * @return true if this folder has a descendant wich the given name, false otherwise
+ */
+ public boolean contains(String descendantName) {
+ staleMarker.check();
+ if (descendantName.startsWith("/")) throw new IllegalArgumentException("child name starts with '/': " + descendantName);
+ DBBroker _broker = null;
+ try {
+ _broker = db.acquireBroker();
+ return _broker.getCollection(XmldbURI.create(path() + "/" + descendantName)) != null;
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ db.releaseBroker(_broker);
+ }
+ }
+
+ /**
+ * Export these children folders to the given directory.
+ * If the directory does not exist it will be created. A subdirectory will be created
+ * for each child folder.
+ *
+ * @param destination the destination folder to export into
+ * @throws IOException if the export failed due to an I/O error
+ */
+ public void export(File destination) throws IOException {
+ if (!destination.exists()) destination.mkdirs();
+ if (!destination.isDirectory()) throw new IOException("export destination not a directory: " + destination);
+ for (Folder child : this) {
+ child.export(new File(destination, child.name()));
+ }
+ }
+
+ public class FolderIterator implements Iterator {
+ private Folder last;
+
+ private Iterator delegate = null;
+ private DBBroker broker = null;
+
+ private Iterator getDelegate() throws IllegalStateException {
+ if(delegate == null) {
+ try {
+ broker = db.acquireBroker();
+ delegate = getQuickHandle().collectionIterator(broker);
+ } catch(PermissionDeniedException pde) {
+ throw new IllegalStateException(pde.getMessage(), pde);
+ }
+ }
+
+ return delegate;
+ }
+
+ @Override
+ public void remove() {
+ staleMarker.check();
+ if (last == null) {
+ throw new IllegalStateException("no collection to remove");
+ }
+ last.delete();
+ last = null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ staleMarker.check();
+ boolean result = getDelegate().hasNext();
+ if(!result) {
+ db.releaseBroker(broker);
+ }
+ return result;
+ }
+
+ @Override
+ public Folder next() {
+ staleMarker.check();
+ last = get(getDelegate().next().getCollectionPath());
+ return last;
+ }
+ }
+
+ /**
+ * Return an iterator over the immediate child subfolders. You can use this iterator to
+ * selectively delete subfolders as well.
+ *
+ * @return an iterator over the child subfolders
+ */
+ @Override
+ public Iterator iterator() {
+ staleMarker.check();
+ return new FolderIterator();
+ }
+
+ }
+
+ /**
+ * The immediate documents facet of a folder. Gives access to the (conceptual) set of
+ * documents contained directly in a folder. All functions will not consider
+ * documents contained in subfolders.
+ *
+ * @author Piotr Kaminski
+ */
+ public class DocumentsFacet extends Resource implements Iterable {
+
+ private ListenersFacet listeners;
+
+ /**
+ * The facet that gives control over listeners for documents contained directly within
+ * a folder.
+ *
+ * @author Piotr Kaminski
+ */
+ public class ListenersFacet {
+
+ /**
+ * Add a listener for all documents directly in this folder. Equivalent to add(EnumSet.of(trigger), listener).
+ *
+ * @see #add(Set, Document.Listener)
+ * @param trigger the kind of event the listener should be notified of
+ * @param listener the listener to notify of events
+ */
+ public void add(Trigger trigger, Document.Listener listener) {
+ add(EnumSet.of(trigger), listener);
+ }
+
+ /**
+ * Add a listener for all documents directly in this folder.
+ *
+ * @param triggers the kinds of events the listener should be notified of; the set must not be empty
+ * @param listener the listener to notify of events
+ */
+ public void add(Set triggers, Document.Listener listener) {
+ staleMarker.check();
+ ListenerManager.INSTANCE.add(path(), ListenerManager.Depth.ONE, triggers, listener, Folder.this);
+ }
+
+ /**
+ * Remove a listener previously added through this facet. This will remove the listener from
+ * all combinations of timing and action for this folder, even if added via multiple invocations
+ * of the add methods. However, it will not remove the listener from combinations
+ * added through other facets.
+ *
+ * @param listener the listener to remove
+ */
+ public void remove(Document.Listener listener) {
+ ListenerManager.INSTANCE.remove(path(), ListenerManager.Depth.ONE, listener);
+ }
+ }
+
+ private DocumentsFacet() {
+ super(Folder.this.namespaceBindings, Folder.this.db);
+ }
+
+ @Override Sequence convertToSequence() {
+ staleMarker.check();
+ return getDocsSequence(false);
+ }
+
+ /**
+ * Return the facet that allows control over listenersof the folder's immediate documents.
+ *
+ * @return the immediate documents' listener facet
+ */
+ public ListenersFacet listeners() {
+ if (listeners == null) listeners = new ListenersFacet();
+ return listeners;
+ }
+
+ /**
+ * Build a new XML document in this collection, with the given relative path. Remember to {@link ElementBuilder#commit commit}
+ * commit the builder when done. If the builder doesn't commit, no document is created.
+ *
+ * @param name the relative path to the new document
+ * @return a builder to use to create the document
+ */
+ public ElementBuilder build(final Name name) {
+ Folder target = name.stripPathPrefix(Folder.this);
+ if (target != Folder.this) return target.documents().build(name);
+
+ staleMarker.check();
+ return new ElementBuilder(Folder.this.namespaceBindings(), false, new ElementBuilder.CompletedCallback() {
+ public XMLDocument completed(Node[] nodes) {
+ assert nodes.length == 1;
+ Node node = nodes[0];
+ transact(Lock.WRITE_LOCK);
+ try {
+ name.setContext(handle);
+ IndexInfo info = handle.validateXMLResource(tx.tx, broker, XmldbURI.create(name.get()), node);
+ changeLock(Lock.NO_LOCK);
+ handle.store(tx.tx, broker, info, node, false);
+ commit();
+ } catch (EXistException e) {
+ throw new DatabaseException(e);
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException(e);
+ } catch (TriggerException e) {
+ throw new DatabaseException(e);
+ } catch (SAXException e) {
+ throw new DatabaseException(e);
+ } catch (LockException e) {
+ throw new DatabaseException(e);
+ } catch (IOException e) {
+ throw new DatabaseException(e);
+ } finally {
+ release();
+ }
+ return get(name.get()).xml();
+ }
+ });
+ }
+
+ /**
+ * Return whether this facet's folder contains a document with the given relative path.
+ *
+ * @param documentPath the relative path of the document to check for
+ * @return true if the folder contains a document with the given path, false otherwise
+ */
+ public boolean contains(String documentPath) {
+ checkIsRelativeDocPath(documentPath);
+ if (documentPath.contains("/")) return database().contains(Folder.this.path() + "/" + documentPath);
+
+ DBBroker _broker = null;
+ try {
+ _broker = db.acquireBroker();
+ return getQuickHandle().getDocument(_broker, XmldbURI.create(documentPath)) != null;
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ if(_broker != null) {
+ db.releaseBroker(_broker);
+ }
+ }
+
+ }
+
+ /**
+ * Create an XML document with the given relative path, takings its contents from the given source.
+ *
+ * @param name the desired relative path of the document
+ * @param source the source of XML data to read in the document contents from; the folder's namespace bindings are not applied
+ * @return the newly created document
+ * @throws DatabaseException if anything else goes wrong
+ */
+ public XMLDocument load(Name name, Source.XML source) {
+ Folder target = name.stripPathPrefix(Folder.this);
+ if (target != Folder.this) return target.documents().load(name, source);
+
+ transact(Lock.WRITE_LOCK);
+ try {
+ source.applyOldName(name);
+ name.setContext(handle);
+ IndexInfo info = handle.validateXMLResource(tx.tx, broker, XmldbURI.create(name.get()), source.toInputSource());
+ changeLock(Lock.NO_LOCK);
+ handle.store(tx.tx, broker, info, source.toInputSource(), false);
+ commit();
+ } catch (EXistException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (TriggerException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (SAXException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (LockException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (IOException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } finally {
+ release();
+ }
+ return get(name.get()).xml();
+ }
+
+ /**
+ * Create a binary document with the given relative path, takings its contents from the given source.
+ *
+ * @param name the desired relative path of the document
+ * @param source the source to read the document contents from
+ * @return the newly created document
+ * @throws DatabaseException if anything else goes wrong
+ */
+ public Document load(Name name, Source.Blob source) {
+ Folder target = name.stripPathPrefix(Folder.this);
+ if (target != Folder.this) return target.documents().load(name, source);
+
+ transact(Lock.WRITE_LOCK);
+ try {
+ source.applyOldName(name);
+ name.setContext(handle);
+ InputStream inputStream = source.toInputStream();
+ try {
+ handle.addBinaryResource(tx.tx, broker, XmldbURI.create(name.get()), inputStream, null, source.getLength());
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (LockException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (TriggerException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } catch (EXistException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } finally {
+ inputStream.close();
+ }
+ commit();
+ } catch (IOException e) {
+ throw new DatabaseException("failed to create document '" + name + "' from source " + source, e);
+ } finally {
+ release();
+ }
+ return get(name.get());
+ }
+
+ /**
+ * Get the contained document with the given relative path.
+ *
+ * @param documentPath the relative path of the document to find
+ * @return the document with the given path
+ * @throws DatabaseException if unable to find or access the desired document
+ */
+ public Document get(String documentPath) {
+ checkIsRelativeDocPath(documentPath);
+ if (documentPath.contains("/")) return database().getDocument(Folder.this.path() + "/" + documentPath);
+
+ DBBroker _broker = null;
+ try {
+ _broker = db.acquireBroker();
+ DocumentImpl dimpl = getQuickHandle().getDocument(_broker, XmldbURI.create(documentPath));
+ if (dimpl == null) throw new DatabaseException("no such document: " + documentPath);
+ return Document.newInstance(dimpl, Folder.this);
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ if(_broker != null) {
+ db.releaseBroker(_broker);
+ }
+ }
+ }
+
+ private void checkIsRelativeDocPath(String docPath) {
+ if (docPath.startsWith("/")) throw new IllegalArgumentException("relative path cannot start with '/': " + docPath);
+ if (docPath.endsWith("/")) throw new IllegalArgumentException("relative path cannot end with '/': " + docPath);
+ }
+
+ /**
+ * Return the number of documents immediately contained in the folder.
+ *
+ * @return the number of child documents
+ */
+ public int size() {
+ DBBroker _broker = null;
+ try {
+ _broker = db.acquireBroker();
+ return getQuickHandle().getDocumentCount(_broker);
+ } catch (PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ db.releaseBroker(_broker);
+ }
+ }
+
+ /**
+ * Export the immediately contained documents to the given directory.
+ * If the directory does not exist it will be created. Documents in this
+ * folder will appear directly in the given directory.
+ *
+ * @param destination the destination folder to export into
+ * @throws IOException if the export failed due to an I/O error
+ */
+ public void export(File destination) throws IOException {
+ if (!destination.exists()) destination.mkdirs();
+ if (!destination.isDirectory()) throw new IOException("export destination not a directory: " + destination);
+ for (Document doc : this) {
+ doc.export(new File(destination, doc.name()));
+ }
+ }
+
+ /**
+ * Query over the documents immediately contained in the folder, ignoring any documents
+ * in subfolders.
+ *
+ * @return a query service over the folder's immediate documents
+ */
+ @Override public QueryService query() {
+ staleMarker.check();
+ return super.query();
+ }
+
+ @Override QueryService createQueryService() {
+ return new QueryService(Folder.this) {
+ @Override
+ protected void prepareContext(DBBroker broker_) {
+ acquire(Lock.READ_LOCK, broker_);
+ try {
+ docs = handle.allDocs(broker_, new DefaultDocumentSet(), false);
+ baseUri = new AnyURIValue(handle.getURI());
+ }catch (PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ release();
+ }
+ }
+ };
+ }
+
+ /**
+ * Return an iterator over the folder's immediate documents. This iterator can be used
+ * to selectively delete documents as well.
+ *
+ * @return an iterator over the folder's immediate documents
+ */
+ public Iterator iterator() {
+ return new Iterator() {
+ private Iterator delegate;
+ private Document last;
+ {
+ acquire(Lock.READ_LOCK);
+ try {
+ delegate = handle.iterator(broker);
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ release();
+ }
+ }
+ public void remove() {
+ staleMarker.check();
+ if (last == null) throw new IllegalStateException("no document to remove");
+ last.delete();
+ last = null;
+ }
+ public boolean hasNext() {
+ staleMarker.check();
+ return delegate.hasNext();
+ }
+ public Document next() {
+ staleMarker.check();
+ last = Document.newInstance(delegate.next(), Folder.this);
+ return last;
+ }
+ };
+ }
+
+ }
+
+ /**
+ * The facet that allwos control over listeners to the folder, and all its descendant folders and
+ * documents.
+ *
+ * @author Piotr Kaminski
+ */
+ public class ListenersFacet {
+
+ /**
+ * Add a listener for either folder or document events on this folder, its contents, and all
+ * its descendants and their contents as well. Equivalent to add(EnumSet.of(trigger), listener).
+ *
+ * @see #add(Set, org.exist.fluent.Listener)
+ * @param trigger the kind of event the listener should be notified of
+ * @param listener the listener to notify of events
+ */
+ public void add(Trigger trigger, org.exist.fluent.Listener listener) {
+ add(EnumSet.of(trigger), listener);
+ }
+
+ /**
+ * Add a listener for either folder or document events on this folder, its contents, and all
+ * its descendants and their contents as well. The listener's type (either {@link Document.Listener}
+ * or {@link Folder.Listener}) will determine the kinds of events it will receive. Note that
+ * if the listener implements both interfaces it will be notified of events that concern both
+ * documents and folders (and satisfy the timing and action characteristics requested).
+ *
+ * @param triggers the kinds of events the listener should be notified of; the set must not be empty
+ * @param listener the listener to notify of events
+ */
+ public void add(Set triggers, org.exist.fluent.Listener listener) {
+ staleMarker.check();
+ ListenerManager.INSTANCE.add(path(), ListenerManager.Depth.MANY, triggers, listener, Folder.this);
+ }
+
+ /**
+ * Remove a listener previously added through this facet. This will remove the listener from
+ * all combinations of timing and action for this folder, even if added via multiple invocations
+ * of the add methods. However, it will not remove the listener from combinations
+ * added through other facets, even this folder's documents facet or the facets of any descendants.
+ *
+ * @param listener the listener to remove
+ */
+ public void remove(org.exist.fluent.Listener listener) {
+ ListenerManager.INSTANCE.remove(path(), ListenerManager.Depth.MANY, listener);
+ }
+ }
+
+
+
+ private String path;
+ private StaleMarker staleMarker;
+ private ChildrenFacet children;
+ private DocumentsFacet documents;
+ private ListenersFacet listeners;
+ private MetadataFacet metadata;
+
+ // the following are only valid while we're holding a broker
+ private DBBroker broker;
+ private Collection handle;
+ private Transaction tx;
+ private int lockMode;
+ private boolean ownBroker;
+
+ /**
+ * Create a wrapper around the given collection.
+ *
+ * @param path the absolute path to the desired collection, must start with '/'
+ * @param createIfMissing what to do if the given collection doesn't exist; if true, create it, otherwise signal an error
+ * @param origin the origin, indicating the database instance and namespace bindings to be extended
+ * @throws DatabaseException if the collection cannot be found and is not supposed to be created
+ */
+ Folder(String path, boolean createIfMissing, Resource origin) {
+ this(path, createIfMissing, origin.namespaceBindings().extend(), origin.database());
+ }
+
+ Folder(String path, boolean createIfMissing, NamespaceMap namespaceBindings, Database db) {
+ super(namespaceBindings, db);
+ if (path.length() == 0 || path.charAt(0) != '/') throw new IllegalArgumentException("path must start with /, got " + path);
+
+ try {
+ broker = db.acquireBroker();
+ Collection collection;
+ if (createIfMissing) {
+ tx = Database.requireTransaction();
+ try {
+ collection = createInternal(path);
+ tx.commit();
+ } finally {
+ tx.abortIfIncomplete();
+ }
+ } else {
+ try {
+ collection = broker.getCollection(XmldbURI.create(path));
+
+ if (collection == null) {
+ throw new DatabaseException("collection not found '" + path + "'");
+ }
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ }
+ }
+ // store the normalized path, minus the root prefix if possible
+ changePath(collection.getURI().getCollectionPath());
+ } finally {
+ db.releaseBroker(broker);
+ broker = null;
+ tx = null;
+ }
+ }
+
+ private void changePath(String newPath) {
+ this.path = Database.normalizePath(newPath);
+ staleMarker = new StaleMarker();
+ staleMarker.track(path);
+ }
+
+ /**
+ * Return this folder's listeners facet, giving control over listeners to events on this folder,
+ * its contents, and all its descendants.
+ *
+ * @return this folder's listeners facet
+ */
+ public ListenersFacet listeners() {
+ if (listeners == null) listeners = new ListenersFacet();
+ return listeners;
+ }
+
+ @Override public MetadataFacet metadata() {
+ if (metadata == null) metadata = new MetadataFacet(getQuickHandle().getPermissionsNoLock(), db) {
+ @Override public Date creationDate() {
+ return new Date(getQuickHandle().getCreationTime());
+ }
+ };
+ return metadata;
+ }
+
+ /**
+ * Create a duplicate handle that copies the original's path and namespace bindings.
+ * No copy is created of the underlying folder. The namespace bindings will copy the
+ * original's immediate namespace map and namespace bindings inheritance chain.
+ *
+ * @return a duplicate of this collection wrapper
+ */
+ @Override
+ public Folder clone() {
+ staleMarker.check();
+ return new Folder(path(), false, namespaceBindings.clone(), db);
+ }
+
+ public Folder cloneWithoutNamespaceBindings() {
+ staleMarker.check();
+ return new Folder(path(), false, new NamespaceMap(), db);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Folder) return path().equals(((Folder) o).path());
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return path().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "folder '" + path() + "'";
+ }
+
+ private Collection createInternal(String targetPath) {
+ try {
+ Collection collection = broker.getOrCreateCollection(tx.tx, XmldbURI.create(targetPath));
+ broker.saveCollection(tx.tx, collection);
+ broker.flush();
+ return collection;
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException(e);
+ } catch (IOException e) {
+ throw new DatabaseException(e);
+ } catch (TriggerException e) {
+ throw new DatabaseException(e);
+ }
+ }
+
+ void transact(int _lockMode) {
+ if (tx != null) throw new IllegalStateException("transaction already in progress");
+ tx = Database.requireTransaction();
+ acquire(_lockMode);
+ }
+
+ void commit() {
+ if (tx == null) throw new IllegalStateException("no transaction in progress");
+ tx.commit(); // later aborts will do nothing
+ }
+
+ void acquire(int _lockMode) {
+ DBBroker _broker = db.acquireBroker();
+ ownBroker = true;
+ try {
+ acquire(_lockMode, _broker);
+ } catch (RuntimeException e) {
+ db.releaseBroker(_broker);
+ ownBroker = false;
+ throw e;
+ }
+ }
+
+ void acquire(int _lockMode, DBBroker _broker) {
+ staleMarker.check();
+ if(broker != null || handle != null) throw new IllegalStateException("broker already acquired");
+ broker = _broker;
+ try {
+ handle = broker.openCollection(XmldbURI.create(path()), _lockMode);
+ if(handle == null) {
+ throw new DatabaseException("collection not found '" + path + "'");
+ }
+ this.lockMode = _lockMode;
+ } catch (RuntimeException e) {
+ broker = null;
+ handle = null;
+ throw e;
+ } catch(PermissionDeniedException pde) {
+ broker = null;
+ handle = null;
+ throw new DatabaseException(pde.getMessage(), pde);
+ }
+ }
+
+ void release() {
+ if (broker == null || handle == null) throw new IllegalStateException("broker not acquired");
+ if (tx != null) tx.abortIfIncomplete();
+ if (lockMode != Lock.NO_LOCK) handle.getLock().release(lockMode);
+ if (ownBroker) db.releaseBroker(broker);
+ ownBroker = false;
+ broker = null;
+ handle = null;
+ tx = null;
+ }
+
+ void changeLock(int newLockMode) {
+ if (broker == null || handle == null) throw new IllegalStateException("broker not acquired");
+ if (lockMode == newLockMode) return;
+ if (lockMode == Lock.NO_LOCK) {
+ try {
+ handle.getLock().acquire(newLockMode);
+ lockMode = newLockMode;
+ } catch (LockException e) {
+ throw new DatabaseException(e);
+ }
+ } else {
+ if (newLockMode != Lock.NO_LOCK) throw new IllegalStateException("cannot change between read and write lock modes");
+ handle.getLock().release(lockMode);
+ lockMode = newLockMode;
+ }
+ }
+
+ private Collection getQuickHandle() {
+ acquire(Lock.NO_LOCK);
+ try {
+ return handle;
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * Return whether this folder is empty, i.e. has no documents or subfolders in it.
+ *
+ * @return whether this folder is empty
+ */
+ public boolean isEmpty() {
+ acquire(Lock.NO_LOCK);
+ try {
+ return handle.getDocumentCount(broker) == 0 && handle.getChildCollectionCount(broker) == 0;
+ } catch (PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ release();
+ }
+
+ }
+
+ /**
+ * Remove all resources and subfolders from this folder, but keep the folder itself.
+ */
+ public void clear() {
+ transact(Lock.READ_LOCK);
+ try {
+ if (handle.getDocumentCount(broker) == 0 && handle.getChildCollectionCount(broker) == 0) return;
+ broker.removeCollection(tx.tx, handle);
+ createInternal(path);
+ commit();
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException(e);
+ } catch (IOException e) {
+ throw new DatabaseException(e);
+ } catch (TriggerException e) {
+ throw new DatabaseException(e);
+ } finally {
+ release();
+ }
+ changePath(path); // reset stale marker
+ }
+
+ /**
+ * Delete this folder, including all documents and descendants. If invoked on the root folder,
+ * deletes all documents and descendants but does not delete the root folder itself.
+ */
+ @Override public void delete() {
+ transact(Lock.NO_LOCK);
+ try {
+ // TODO: temporary hack, remove once removing root collection works in eXist
+ if (path.equals("/")) {
+ for (Iterator it = handle.iterator(broker); it.hasNext();) {
+ DocumentImpl dimpl = it.next();
+ if (dimpl instanceof BinaryDocument) {
+ handle.removeBinaryResource(tx.tx, broker, dimpl);
+ } else {
+ handle.removeXMLResource(tx.tx, broker, dimpl.getFileURI());
+ }
+ }
+ }
+ // end hack
+ broker.removeCollection(tx.tx, handle);
+ commit();
+ } catch (PermissionDeniedException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new DatabaseException(e);
+ } catch (LockException e) {
+ throw new DatabaseException(e);
+ } catch (TriggerException e) {
+ throw new DatabaseException(e);
+ } finally {
+ release();
+ }
+ }
+
+ /**
+ * Move this folder to another spot in the folder hierarchy, possibly changing its name in the process.
+ * This folder will refer to the newly relocated folder when this method returns. You can use this
+ * method to move a folder without changing its name (folder.move(destFolder, Name.keepCreate()))
+ * or to rename it without changing its location (folder.move(folder.parent(), Name.create(newName))).
+ *
+ * @param destination the new parent folder of this folder
+ * @param name the new name for this folder
+ */
+ @Override public void move(Folder destination, Name name) {
+ changePath(moveOrCopyThisFolder(destination, name, false));
+ }
+
+ /**
+ * Copy this folder to another place in the folder hierarchy, returning the newly copied folder
+ * with namespace bindings inherited from this one.
+ *
+ * @param destination the desired parent folder of the copy
+ * @param name the desired name of the copy
+ * @return a reference to the copied folder
+ */
+ @Override public Folder copy(Folder destination, Name name) {
+ return new Folder(moveOrCopyThisFolder(destination, name, true), false, this);
+ }
+
+ private String moveOrCopyThisFolder(Folder destination, Name name, boolean copy) {
+ db.checkSame(destination);
+ transact(Lock.WRITE_LOCK);
+ try {
+ destination.acquire(Lock.WRITE_LOCK, broker);
+ try {
+ name.setOldName(name());
+ name.setContext(handle);
+ if (copy) {
+ broker.copyCollection(tx.tx, handle, destination.handle, XmldbURI.create(name.get()));
+ } else {
+ broker.moveCollection(tx.tx, handle, destination.handle, XmldbURI.create(name.get()));
+ }
+ commit();
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException(e);
+ } catch (LockException e) {
+ throw new DatabaseException(e);
+ } catch (IOException e) {
+ throw new DatabaseException(e);
+ } catch (TriggerException e) {
+ throw new DatabaseException(e);
+ } catch (EXistException e) {
+ throw new DatabaseException(e);
+ } finally {
+ destination.release();
+ }
+ } finally {
+ release();
+ }
+ return destination.path() + "/" + name.get();
+ }
+
+ /**
+ * Provide access to the documents facet of this folder. The documents facet is
+ * the conceptual set of documents contained directly in this collection (and therefore
+ * excludes documents contained in any subfolders).
+ *
+ * @return the documents facet
+ */
+ public DocumentsFacet documents() {
+ if (documents == null) documents = new DocumentsFacet();
+ return documents;
+ }
+
+ /**
+ * Return whether this folder or one of its descendants contains the given named resource.
+ *
+ * @param res the resource to check for
+ * @return true if the resource is contained (directly or indirectly) in this folder, false otherwise
+ */
+ public boolean contains(NamedResource res) {
+ staleMarker.check();
+ db.checkSame(res);
+ return res.path().startsWith(path() + (path().equals("/") ? "" : "/"));
+ }
+
+ /**
+ * Return the given path stripped of this folder's prefix path, i.e. relative to this folder.
+ * The given path can be either to a document or to a subfolder. If the given path is to
+ * this folder itself, return the empty string.
+ *
+ * @param subPath the path to relativize
+ * @return the given path relative to this folder
+ */
+ public String relativePath(String subPath) {
+ if (subPath.equals(path())) return "";
+ if (path().equals("/") && subPath.charAt(0) == '/') return subPath.substring(1);
+ if (!subPath.startsWith(path()+"/")) throw new IllegalArgumentException("path '" + subPath + "' does not fall under this collection's path '" + path() + "'");
+ return subPath.substring(path().length() + 1);
+ }
+
+ /**
+ * Return the full path to the folder; always starts with '/'.
+ *
+ * @return the full path to the folder
+ */
+ @Override public String path() {
+ return path;
+ }
+
+ /**
+ * Return the local name of the folder; this is the last segment of its path.
+ *
+ * @return the local name of the folder
+ */
+ @Override public String name() {
+ return path().substring(path().lastIndexOf('/')+1);
+ }
+
+ /**
+ * Return the parent folder of this folder. It will inherit this folder's namespace bindings.
+ *
+ * @return the parent folder that this folder is a child of
+ * @throws DatabaseException if this is the root folder
+ */
+ public Folder parent() {
+ String parentPath = getQuickHandle().getURI().removeLastSegment().getCollectionPath();
+ if (parentPath == null || parentPath.length() == 0) throw new DatabaseException("this is the root collection");
+ return new Folder(parentPath, false, this);
+ }
+
+ /**
+ * Return the children facet of this folder that gives access to operations on its subfolders
+ * and descendants.
+ *
+ * @return the children facet of this folder
+ */
+ public ChildrenFacet children() {
+ if (children == null) children = new ChildrenFacet();
+ return children;
+ }
+
+ /**
+ * Export the contents of this folder (both documents and subfolders) to the given
+ * directory. If the directory does not exist it will be created. Documents in this
+ * folder will appear directly in the given directory, i.e. a subdirectory matching this
+ * folder will not be created.
+ *
+ * @param destination the destination folder to export into
+ * @throws IOException if the export failed due to an I/O error
+ */
+ public void export(File destination) throws IOException {
+ documents().export(destination);
+ children().export(destination);
+ }
+
+ @Override Sequence convertToSequence() {
+ staleMarker.check();
+ return getDocsSequence(true);
+ }
+
+ private Sequence getDocsSequence(boolean recursive) {
+ try {
+ DocumentSet docs;
+ acquire(Lock.READ_LOCK);
+ try {
+ docs = handle.allDocs(broker, new DefaultDocumentSet(), recursive);
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ release();
+ }
+ Sequence result = new ExtArrayNodeSet(docs.getDocumentCount(), 1);
+ for (Iterator> i = docs.getDocumentIterator(); i.hasNext();) {
+ result.add(new NodeProxy((DocumentImpl) i.next()));
+ }
+ return result;
+ } catch (XPathException e) {
+ throw new RuntimeException("unexpected exception converting document set to sequence", e);
+ }
+ }
+
+ /**
+ * Return a query service for executing queries over the contents of this folder and the contents
+ * of all its descendants. If you only want to query the documents contained directly in this folder,
+ * see {@link #documents() the documents facet}.
+ *
+ * @return a query service over this folder's contents and all its descendants' contents
+ */
+ @Override public QueryService query() {
+ staleMarker.check();
+ return super.query();
+ }
+
+ @Override QueryService createQueryService() {
+ return new QueryService(this) {
+ @Override void prepareContext(DBBroker broker_) {
+ acquire(Lock.READ_LOCK, broker_);
+ try {
+ docs = handle.allDocs(broker_, new DefaultDocumentSet(), true);
+ baseUri = new AnyURIValue(handle.getURI());
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ release();
+ }
+ }
+ };
+ }
+
+ void removeDocument(DocumentImpl dimpl) {
+ transact(Lock.WRITE_LOCK);
+ try {
+ if (dimpl instanceof BinaryDocument) {
+ handle.removeBinaryResource(tx.tx, broker, dimpl.getFileURI());
+ } else {
+ handle.removeXMLResource(tx.tx, broker, dimpl.getFileURI());
+ }
+ commit();
+ } catch (PermissionDeniedException e) {
+ throw new DatabaseException(e);
+ } catch (LockException e) {
+ throw new DatabaseException(e);
+ } catch (TriggerException e) {
+ throw new DatabaseException(e);
+ } finally {
+ release();
+ }
+ }
+
+ DocumentImpl moveOrCopyDocument(DocumentImpl doc, Name name, boolean copy) {
+ XmldbURI uri;
+ transact(Lock.WRITE_LOCK);
+ try {
+ name.setContext(handle);
+ uri = XmldbURI.create(name.get());
+ if (copy) {
+ tx.lockRead(doc);
+ broker.copyResource(tx.tx, doc, handle, uri);
+ } else {
+ tx.lockWrite(doc);
+ broker.moveResource(tx.tx, doc, handle, uri);
+ }
+ commit();
+ } catch (PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } catch (LockException e) {
+ throw new DatabaseException("lock denied", e);
+ } catch (IOException e) {
+ throw new DatabaseException(e);
+ } catch (TriggerException e) {
+ throw new DatabaseException(e);
+ } catch (EXistException e) {
+ throw new DatabaseException(e);
+ } finally {
+ release();
+ }
+
+ DBBroker _broker = null;
+ Collection _handle = getQuickHandle();
+ try {
+ _broker = db.acquireBroker();
+ return _handle.getDocument(_broker, uri);
+ } catch(PermissionDeniedException pde) {
+ throw new DatabaseException(pde.getMessage(), pde);
+ } finally {
+ if(_broker != null) {
+ db.releaseBroker(_broker);
+ }
+ }
+
+
+ }
}
\ No newline at end of file
diff --git a/extensions/fluent/src/org/exist/fluent/Item.java b/extensions/fluent/src/org/exist/fluent/Item.java
index 29a4efcc2ee..a6dc9f9fef1 100644
--- a/extensions/fluent/src/org/exist/fluent/Item.java
+++ b/extensions/fluent/src/org/exist/fluent/Item.java
@@ -1,333 +1,333 @@
-package org.exist.fluent;
-
-import java.util.Date;
-
-import javax.xml.datatype.Duration;
-import javax.xml.datatype.XMLGregorianCalendar;
-
-import org.exist.dom.NodeProxy;
-import org.exist.storage.DBBroker;
-import org.exist.storage.serializers.Serializer;
-import org.exist.xquery.Constants;
-import org.exist.xquery.XPathException;
-import org.exist.xquery.value.*;
-import org.xml.sax.SAXException;
-
-/**
- * An XML item in the database. While most often used to represent XML
- * elements, it can also stand in for any DOM node or an atomic value. However, it
- * is not used to represent entire XML documents (see {@link org.exist.fluent.XMLDocument}).
- * Not all operations are valid in all cases.
- *
- * @author Piotr Kaminski
- * @version $Revision: 1.13 $ ($Date: 2006/04/14 04:12:04 $)
- */
-public class Item extends Resource {
-
- protected final org.exist.xquery.value.Item item;
-
- Item() {
- super(null, null);
- this.item = null;
- }
-
- Item(org.exist.xquery.value.Item item, NamespaceMap namespaceBindings, Database db) {
- super(namespaceBindings, db);
- // the item should've been tracked (Database.trackNode) before getting here!
- this.item = item;
- }
-
- /**
- * Return this item cast as a node.
- *
- * @return this item cast as a node
- * @throws DatabaseException if this item is not a node
- */
- public Node node() {
- throw new DatabaseException("this item is not a node: " + this);
- }
-
- @Override public boolean equals(Object o) {
- if (!(o instanceof Item)) return false;
- Item that = (Item) o;
- if (this.item == that.item) return true;
- if (this.item instanceof AtomicValue && that.item instanceof AtomicValue) {
- AtomicValue thisValue = (AtomicValue) this.item, thatValue = (AtomicValue) that.item;
- try {
- return
- thisValue.getType() == thatValue.getType()
- && thisValue.compareTo(null, Constants.EQ, thatValue);
- } catch (XPathException e) {
- // fall through
- }
- }
- return false;
- }
-
- /**
- * The hash code computation can be expensive, and the hash codes may not be very well distributed.
- * You probably shouldn't use items in situations where they might get hashed.
- */
- @Override public int hashCode() {
- if (item instanceof AtomicValue) {
- AtomicValue value = (AtomicValue) item;
- try {
- return value.getType() ^ value.getStringValue().hashCode();
- } catch (XPathException e) {
- return value.getType();
- }
- } else {
- return item.hashCode();
- }
- }
-
- /**
- * Return the type of this item, e.g. element() or xs:string.
- *
- * @return the type of this item
- */
- public String type() {
- return Type.getTypeName(item.getType());
- }
-
- /**
- * Return whether this item really exists. Examples of items that don't exist even though they have an object
- * representing them include virtual placeholders returned for an optional query that didn't select an item, and
- * items that were deleted from the database after being selected.
- *
- * @return true if the item exists, false otherwise
- */
- public boolean extant() {
- return true;
- }
-
- @Override Sequence convertToSequence() {
- return item.toSequence();
- }
-
- /**
- * Return a singleton item list consisting of this item.
- *
- * @return an item list that contains only this item
- */
- public ItemList toItemList() {
- return new ItemList(convertToSequence(), namespaceBindings.extend(), db);
- }
-
- /**
- * Atomize this item and return the result. This is useful if you don't know if you're
- * holding a node or an atomic item, and want to ensure it's an atomic value without
- * potentially losing its type by converting it to a string.
- *
- * @return the atomized value of this item; may be this item itself if it's already atomic
- */
- public Item toAtomicItem() {
- try {
- org.exist.xquery.value.Item atomizedItem = item.atomize();
- return atomizedItem == item ? this : new Item(atomizedItem, namespaceBindings.extend(), db);
- } catch (XPathException e) {
- throw new DatabaseException("unable to atomize item", e);
- }
- }
-
- /**
- * @return the string value of this item if atomic, or the concatenation of its text content if a node
- */
- public String value() {
- try {
- return item.getStringValue();
- } catch (XPathException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * @param defaultValue the default value to return if the item is null (unbound)
- * @return the string value of this item if atomic, or the concatenation of its text content if a node,
- * or the given default value if this is a null item
- */
- public String valueWithDefault(String defaultValue) {
- return value();
- }
-
- /**
- * Return the converted boolean value following XQuery / XPath conversion rules.
- * For numeric values, return false iff the value is 0. For strings, return true if
- * the value is 'true' or '1' and false if the value is 'false' or '0', fail otherwise. For
- * nodes, return the conversion of the effective string value.
- *
- * @return the boolean value of the item
- * @throws DatabaseException if the conversion failed
- */
- public boolean booleanValue() {
- try {
- return ((Boolean) item.toJavaObject(Boolean.class)).booleanValue();
- } catch (XPathException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * Return the int value of this item. For numeric atomic values, truncate to an int;
- * for other values, request a conversion of the effective string value (which may fail).
- * If the value is out of range for ints, return the smallest or largest int, as appropriate.
- * If you think overflow may be a problem, check for these values.
- *
- * @return the int value of this item
- * @throws DatabaseException if the conversion failed
- */
- public int intValue() {
- try {
- return ((Integer) item.toJavaObject(Integer.class)).intValue();
- } catch (XPathException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * Return the long value of this item. For numeric atomic values, truncate to a long;
- * for other values, request a conversion of the effective string value (which may fail).
- * If the value is out of range for longs, return the smallest or largest long, as appropriate.
- * If you think overflow may be a problem, check for these values.
- *
- * @return the long value of this item
- * @throws DatabaseException if the conversion failed
- */
- public long longValue() {
- try {
- return ((Long) item.toJavaObject(Long.class)).longValue();
- } catch (XPathException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * Return the double value of this item. For numeric atomic values, truncate to a double;
- * for other values, request a conversion of the effective string value (which may fail).
- * If the value is out of range for doubles, return positive or negative infinity, as appropriate.
- * If you think overflow may be a problem, check for these values.
- *
- * @return the double value of this item
- * @throws DatabaseException if the conversion failed
- */
- public double doubleValue() {
- try {
- return ((Double) item.toJavaObject(Double.class)).doubleValue();
- } catch (XPathException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * Return the duration value of this item by parsing its string representation as a duration.
- *
- * @return the duration value of this item
- * @throws DatabaseException if the conversion failed
- */
- public Duration durationValue() {
- try {
- return DataUtils.datatypeFactory().newDuration(value());
- } catch (IllegalArgumentException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * Return the XML date/time value of this item by parsing its string representation.
- *
- * @return the XML date/time value of this item
- * @throws DatabaseException if the conversion failed
- */
- public XMLGregorianCalendar dateTimeValue() {
- try {
- return DataUtils.datatypeFactory().newXMLGregorianCalendar(value());
- } catch (IllegalArgumentException e) {
- throw new DatabaseException(e);
- }
- }
-
- /**
- * Return the java.util.Date value of this item by parsing its string
- * representation as an XML date/time value, then converting to a Java date.
- *
- * @return the Java time instant value of this item
- * @throws DatabaseException if the conversion failed
- */
- public Date instantValue() {
- return DataUtils.toDate(dateTimeValue());
- }
-
- /**
- * Return the comparable value of this item, if available. The resulting object will
- * directly compare the XQuery value without converting to a string first, unless the
- * item is untyped and atomic, in which case it will be cast to a string. Items of
- * different types will compare in an arbitrary but stable order. Nodes are never
- * comparable.
- *
- * @return the comparable value of this item
- * @throws DatabaseException if this item is not comparable
- */
- @SuppressWarnings("unchecked")
- public Comparable