package net.pms.dlna;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GlobalIdRepo {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalIdRepo.class);
// Global ids start at 1, since id 0 is reserved as a pseudonym for 'renderer root'
private int curGlobalId = 1, deletionsCount = 0;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final ArrayList<ID> ids = new ArrayList<>();
private static class ID {
final int id;
final DLNAResource dlnaResource;
private ID(DLNAResource dlnaResource, int id) {
this.id = id;
this.dlnaResource = dlnaResource;
dlnaResource.setIndexId(id);
}
}
public GlobalIdRepo() {
}
public void add(DLNAResource dlnaResource) {
lock.writeLock().lock();
try {
String id = dlnaResource.getId();
if (id != null) {
remove(id);
}
ids.add(new ID(dlnaResource, curGlobalId++));
} finally {
lock.writeLock().unlock();
}
}
public DLNAResource get(String id) {
return get(parseIndex(id));
}
public DLNAResource get(int id) {
lock.readLock().lock();
try {
int index = indexOf(id);
return index > -1 ? ids.get(index).dlnaResource : null;
} finally {
lock.readLock().unlock();
}
}
public void remove(DLNAResource d) {
remove(d.getId());
}
public void remove(String id) {
remove(parseIndex(id));
}
public void remove(int id) {
lock.writeLock().lock();
try {
int index = indexOf(id);
if (index > -1) {
LOGGER.debug("GlobalIdRepo: removing id {} - {}", id, ids.get(index).dlnaResource.getName());
ids.remove(index);
deletionsCount++;
}
} finally {
lock.writeLock().unlock();
}
}
public static int parseIndex(String id) {
try {
// Id strings may have optional tags beginning with $ appended, e.g. '1234$Temp'
return Integer.parseInt(StringUtils.substringBefore(id, "$"));
} catch (NumberFormatException e) {
return -1;
}
}
public boolean exists(String id) {
return indexOf(parseIndex(id)) != -1;
}
private int indexOf(int id) {
lock.readLock().lock();
try {
if (id > 0 && id < curGlobalId) {
// We're in sequence by definition, so binary search is quickest
// Exclude any areas where the id can't possibly be
int ceil = ids.size() - 1;
int top = id - 1; // id 0 is reserved, so index is id-1 at most
int hi = top < ceil ? top : ceil;
int floor = hi - deletionsCount;
int lo = floor > 0 ? floor : 0;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
int idm = ids.get(mid).id;
if (id < idm) {
hi = mid - 1;
} else if (id > idm) {
lo = mid + 1;
} else {
return mid;
}
}
}
} finally {
lock.readLock().unlock();
}
LOGGER.debug("GlobalIdRepo: id not found: {}", id);
return -1;
}
}