/*
* Digital Audio Access Protocol (DAAP) Library
* Copyright (C) 2004-2010 Roger Kapsi
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ardverk.daap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* The Transaction object is the handle for a transaction. Methods off the
* transaction handle are used to abort and commit the transaction.
*
* @author Roger Kapsi
*/
public class Transaction {
protected final Map<Object, List<Txn>> txnMap
= new HashMap<Object, List<Txn>>();
protected final Library library;
protected boolean open = false;
protected Transaction(Library library) {
this.library = library;
this.open = true;
}
public Library getLibrary() {
return library;
}
/**
* Returns <code>true</code> if this Transaction is open what means that it
* hasn't been commited yet.
*
* @return <code>true</code> if this Transaction is open
*/
public synchronized boolean isOpen() {
return open;
}
/**
* Commit this Transaction
*/
public synchronized void commit() {
if (!isOpen())
throw new DaapException("Transaction is not open");
try {
if (!txnMap.isEmpty()) {
synchronized (library) {
for (List<Txn> list : txnMap.values()) {
for (Txn txn : list) {
txn.commit(this);
}
}
library.commit(this);
}
}
} finally {
close();
}
}
/**
* Rollback this Transaction
*/
public synchronized void rollback() {
if (!isOpen())
throw new DaapException("Transaction is not open");
try {
if (!txnMap.isEmpty()) {
synchronized (library) {
for (List<Txn> list : txnMap.values()) {
for (Txn txn : list) {
txn.rollback(this);
}
}
library.rollback(this);
}
}
} finally {
close();
}
}
/**
* Attaches Object and Txn to this Transaction. Object must be an instance
* of Song, Playlist, Database or Library!
*/
protected synchronized void addTxn(Object obj, Txn txn) {
// if (!isOpen()) {
// throw new DaapException("Transaction is not open");
// }
List<Txn> list = txnMap.get(obj);
if (list == null || list == Collections.EMPTY_LIST) {
list = new ArrayList<Txn>();
txnMap.put(obj, list);
}
list.add(txn);
}
/**
* Attach Object to Transaction. This is necessary for Objects that were
* constructed/modified independently from this transaction.
*/
@SuppressWarnings("unchecked")
protected synchronized void attach(Object obj) {
if (!txnMap.containsKey(obj)) {
txnMap.put(obj, Collections.EMPTY_LIST);
}
}
/**
* Returns true if Library or one of its Databases was modified
*/
@SuppressWarnings("unchecked")
protected synchronized boolean modified(Library library) {
if (txnMap.containsKey(library)) {
return true;
}
for (Database database : library.getDatabases()) {
if (modified(database)) {
txnMap.put(library, Collections.EMPTY_LIST);
return true;
}
}
return false;
}
/**
* Returns true if Database or one of its Playlists was modified
*/
@SuppressWarnings("unchecked")
protected synchronized boolean modified(Database database) {
if (txnMap.containsKey(database)) {
return true;
}
Iterator<Playlist> it = database.getPlaylists().iterator();
while (it.hasNext()) {
if (modified(it.next())) {
txnMap.put(database, Collections.EMPTY_LIST);
return true;
}
}
return false;
}
/**
* Returns true if Playlist or one of its Songs was modified
*/
protected synchronized boolean modified(Playlist playlist) {
if (txnMap.containsKey(playlist)) {
return true;
}
for (Song song : playlist.getSongs()) {
if (modified(song)) {
List<Txn> empty = Collections.emptyList();
txnMap.put(playlist, empty);
return true;
}
}
return false;
}
/**
* Returns true if Song was modified
*/
protected synchronized boolean modified(Song song) {
return txnMap.containsKey(song);
}
/**
* Cleanup
*/
protected void close() {
if (open) {
library.close(this);
open = false;
if (txnMap != null) {
txnMap.clear();
}
}
}
@Override
public String toString() {
return "Transaction: " + library;
}
}