Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.objectfabric.Resource Maven / Gradle / Ivy
/**
* This file is part of ObjectFabric (http://objectfabric.org).
*
* ObjectFabric is licensed under the Apache License, Version 2.0, the terms
* of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
*
* Copyright ObjectFabric Inc.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.objectfabric;
import java.util.Map.Entry;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
/**
* A resource can be anything from a text string to a complex object graph. Objects from a
* resource can only reference objects belonging to the same resource.
*/
public class Resource extends TObject {
public static final TType TYPE;
static {
TYPE = Platform.newTType(Platform.get().defaultObjectModel(), BuiltInClass.RESOURCE_CLASS_ID);
}
static final Object NULL = new Object() {
@Override
public String toString() {
return "Resource.NULL";
}
};
private final Workspace _workspace;
private final URI _uri;
private final AtomicReference _loadQueue = new AtomicReference();
//
private volatile Permission _permission;
// TODO use long[] + block array?
private PlatformMap _pending;
// TODO bench merging this and _pending in concurrent map & load on IO threads
private long[] _loaded = new long[OpenMap.CAPACITY];
//
private final List _ordered = new List();
private volatile List _goals = new List();
//
private final PlatformMap _pendingAcks = new PlatformMap();
Resource(Workspace workspace, URI uri) {
super(null, new ResourceVersion());
if (Debug.ENABLED)
if (uri == null)
Debug.assertion(workspace.emptyResource() == null);
_workspace = workspace;
_uri = uri;
if (uri != null)
setReferencedByURI();
}
// For .NET
void onReferenced(PlatformRef ref) {
}
public final URI uri() {
return _uri;
}
/**
* Location this resource was loaded from.
*/
public final Origin origin() {
return _uri.origin();
}
public final Permission permission() {
return _permission;
}
//
final Workspace workspaceImpl() {
return _workspace;
}
final Watcher watcher() {
return _workspace.watcher();
}
final Transaction transaction() {
return _workspace.transactionThreadLocal().get();
}
final PlatformMap pendingAcks() {
return _pendingAcks;
}
final PlatformMap pending() {
return _pending;
}
final List ordered() {
return _ordered;
}
final long[] loaded() {
return _loaded;
}
//
public final boolean isLoaded() {
if (_uri == null)
wrongResource_();
return _goals == null;
}
/**
* Gets resource synchronously. The calling thread blocks until the first location
* responds (origin or cache). Returned value might not be the most up to date
* version, and change when following responses are received from other locations.
*/
public Object get() {
if (_uri == null)
wrongResource_();
for (;;) {
Object value = getFromMemory();
if (value != null)
return value != NULL ? value : null;
FutureWithCallbacks future = _uri.open(this);
if (future != null) {
try {
return future.get();
} catch (Exception ex) {
ExpectedExceptionThrower.throwRuntimeException(ex);
}
}
}
}
public Future getAsync(AsyncCallback callback) {
if (_uri == null)
wrongResource_();
Executor executor = workspaceImpl().callbackExecutor();
for (;;) {
Object value = getFromMemory();
if (value != null) {
value = value != NULL ? value : null;
if (callback == null)
return new CompletedFuture(value);
FutureWithCallback future = new FutureWithCallback(callback, executor);
future.set(value);
return future;
}
FutureWithCallbacks future = _uri.open(this);
if (future != null) {
if (callback != null)
future.addCallback(callback, executor);
return future;
}
}
}
final Object getFromMemory() {
Transaction outer = current_();
Transaction inner = startRead_(outer);
Object value = getFromMemory(inner, (ResourceVersion) inner.getVersion(this), true);
endRead_(outer, inner);
return value;
}
/**
* Updates resource lazily. Calling thread does not block.
*/
public void set(Object value) {
if (_uri == null)
wrongResource_();
if (value == null)
value = NULL;
Transaction outer = current_();
Transaction inner = startWrite_(outer);
ResourceVersion version = (ResourceVersion) getOrCreateVersion_(inner);
boolean open = getFromMemory(inner, version, false) == null;
version._value = value;
endWrite_(outer, inner);
if (open)
_uri.open(this);
}
/**
* Deletes resource lazily. Calling thread does not block.
*/
public void delete() {
set(NULL);
}
private final Object getFromMemory(Transaction transaction, ResourceVersion version, boolean markRead) {
if (version != null && version._value != null)
return version._value;
Version[][] versions = transaction.getPrivateSnapshotVersions();
if (versions != null) {
for (int i = versions.length - 1; i >= 0; i--) {
ResourceVersion v = (ResourceVersion) TransactionBase.getVersion(versions[i], this);
if (v != null && v._value != null)
return v._value;
}
}
if (markRead && !transaction.ignoreReads()) {
Version read = transaction.getRead(this);
if (read == null) {
read = createRead();
transaction.putRead(read);
}
if (Debug.ENABLED)
Debug.assertion(read instanceof ResourceRead);
}
versions = transaction.getPublicSnapshotVersions();
return getFromPublicVersions(versions);
}
private final Object getFromPublicVersions(Version[][] versions) {
for (int i = versions.length - 1; i > TransactionManager.OBJECTS_VERSIONS_INDEX; i--) {
ResourceVersion version = (ResourceVersion) TransactionBase.getVersion(versions[i], this);
if (version != null && version._value != null)
return version._value;
}
ResourceVersion shared = (ResourceVersion) shared_();
return shared._value;
}
//
/**
* Blocks until resource has been synchronized from workspace to given location.
*/
void push(Location location) {
@SuppressWarnings("unchecked")
Future future = pushAsync(location, FutureWithCallback.NOP_CALLBACK);
try {
future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// TODO
Future pushAsync(Location location, AsyncCallback callback) {
if (workspaceImpl().resolver().isClosing())
throw new ClosedException();
return null;
}
/**
* Blocks until resource has been synchronized from given location to workspace.
*/
void pull(Location location) {
@SuppressWarnings("unchecked")
Future future = pullAsync(location, FutureWithCallback.NOP_CALLBACK);
try {
future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// TODO
Future pullAsync(Location location, AsyncCallback callback) {
if (workspaceImpl().resolver().isClosing())
throw new ClosedException();
return null;
}
/*
* IO threads.
*/
final void onPermission(Permission permission) {
if (Debug.ENABLED)
Debug.assertion(permission != null);
_permission = permission;
if (permission == Permission.REJECT)
_uri.onCancel(this, new RuntimeException(Strings.PERMISSION_REJECT));
// Forget acks if permission is not WRITE
if (permission != Permission.WRITE) {
watcher().actor().addAndRun(new Actor.Message() {
@Override
void run(Actor actor) {
List toRemove = null;
for (Entry entry : _pendingAcks.entrySet()) {
if (entry.getValue().PendingAcksBitSet == URIResolver.ORIGIN_BIT) {
if (toRemove == null)
toRemove = new List();
toRemove.add(entry.getKey());
}
}
if (toRemove != null)
for (int i = 0; i < toRemove.size(); i++)
removePendingAck(toRemove.get(i));
}
});
}
}
final void onUnresolved() {
_uri.onCancel(this, new RemoteException(Strings.URI_UNRESOLVED));
}
final void getKnown() {
watcher().actor().addAndRun(new Actor.Message() {
@Override
void run(Actor actor) {
if (_pendingAcks.size() > 0)
tellKnown();
}
});
}
final void tellKnown() {
long[] ticks = null;
for (int i = 0; i < _ordered.size(); i++)
ticks = Tick.add(ticks, _ordered.get(i).Tick);
Object key;
if (Debug.THREADS)
ThreadAssert.suspend(key = new Object());
_uri.onKnown(Resource.this, ticks);
if (Debug.THREADS)
ThreadAssert.resume(key);
}
final void onKnown(final long[] ticks) {
watcher().actor().addAndRun(new Actor.Message() {
@Override
void run(Actor actor) {
if (ticks.length == 0)
onUpToDate();
else {
boolean pending = false;
for (int i = 0; i < ticks.length; i++) {
if (!Tick.isNull(ticks[i])) {
boolean skip = Tick.happenedBefore(ticks[i], _loaded);
if (!skip)
if (_pending != null && _pending.containsKey(ticks[i]))
skip = true;
if (!skip) {
pending = true;
Object key;
if (Debug.THREADS)
ThreadAssert.suspend(key = new Object());
_uri.getBlock(Resource.this, ticks[i]);
if (Debug.THREADS)
ThreadAssert.resume(key);
}
}
}
if (pending) {
List goals = _goals;
if (goals != null)
goals.add(ticks);
}
}
}
});
}
final void getBlock(final long tick) {
if (Debug.ENABLED)
Debug.assertion(!Tick.isNull(tick));
watcher().actor().addAndRun(new Actor.Message() {
@Override
void run(Actor actor) {
NewBlock block = _pendingAcks.get(tick);
if (block != null) {
Buff[] duplicates = new Buff[block.Buffs.length];
for (int i = 0; i < duplicates.length; i++) {
duplicates[i] = block.Buffs[i].duplicate();
if (Debug.THREADS)
ThreadAssert.exchangeGive(duplicates, duplicates[i]);
}
Object key;
if (Debug.THREADS)
ThreadAssert.suspend(key = new Object());
_uri.onBlock(Resource.this, tick, duplicates, null, true, null, true);
if (Debug.THREADS) {
ThreadAssert.resume(key);
ThreadAssert.exchangeTake(duplicates);
}
for (int i = 0; i < duplicates.length; i++)
duplicates[i].recycle();
}
}
});
}
final void onBlock(final Block block) {
if (!watcher().actor().isClosed()) {
for (;;) {
Block head = _loadQueue.get();
block.Next = head;
if (_loadQueue.compareAndSet(head, block)) {
if (head == null) {
watcher().actor().addAndRun(new Actor.Message() {
@Override
void run(Actor actor) {
onEnqueued();
}
});
}
break;
}
}
}
}
final void onAck(final View view, final long tick) {
if (Debug.ENABLED)
Debug.assertion(!Tick.isNull(tick));
watcher().actor().addAndRun(new Actor.Message() {
@Override
void run(Actor actor) {
NewBlock block = _pendingAcks.get(tick);
if (block != null) {
Location[] caches = _workspace.caches();
// Wait for ack of caches if present
if (caches != null) {
for (int i = 0; i < caches.length; i++) {
if (view.location() == caches[i]) {
block.PendingAcksBitSet = Bits.unset(block.PendingAcksBitSet, i);
break;
}
}
int mask = -1 >>> (32 - caches.length);
if ((block.PendingAcksBitSet & mask) == 0)
removePendingAck(tick);
} else {
// Otherwise wait for origin ack
if (view.location() == uri().origin()) {
block.PendingAcksBitSet &= ~URIResolver.ORIGIN_BIT;
if (block.PendingAcksBitSet == 0)
removePendingAck(tick);
}
}
}
}
});
}
final void onFailed(final Object source, long tick) {
watcher().actor().addAndRun(new Actor.Message() {
@Override
void run(Actor actor) {
// If data loss, show currently loaded instead of just blocking
onUpToDate();
}
});
}
/*
* Loading.
*/
final void onEnqueued() {
Block queue;
for (;;) {
queue = _loadQueue.get();
if (_loadQueue.compareAndSet(queue, null))
break;
}
if (Debug.ENABLED)
Debug.assertion(queue != null);
while (queue != null) {
if (!Tick.happenedBefore(queue.Tick, _loaded)) {
if (_pending == null)
_pending = new PlatformMap();
_pending.put(queue.Tick, queue);
}
queue = queue.Next;
}
if (_pending != null) {
long[] missing = null;
int delta = 0;
for (Block block : _pending.values()) {
if (block.Dependencies != null) {
for (int i = 0; i < block.Dependencies.length; i++) {
if (!Tick.isNull(block.Dependencies[i])) {
if (!Tick.happenedBefore(block.Dependencies[i], _loaded)) {
if (!_pending.containsKey(block.Dependencies[i])) {
if (!Tick.contains(missing, block.Dependencies[i])) {
missing = Tick.add(missing, block.Dependencies[i]);
delta++;
}
}
}
}
}
}
}
for (int i = 0; missing != null && i < missing.length; i++) {
if (!Tick.isNull(missing[i])) {
for (Block block : _pending.values()) {
if (replaces(block, missing[i])) {
missing[i] = Tick.REMOVED;
delta--;
break;
}
}
}
}
List list = new List();
if (delta == 0) {
for (Block block : _pending.values())
list.add(block);
_pending = null;
} else {
for (Block block : _pending.values())
if (!hasMissingDependencies(block, missing))
list.add(block);
for (int i = 0; i < list.size(); i++)
_pending.remove(list.get(i).Tick);
if (_pending.size() == 0)
_pending = null;
}
if (list.size() > 0) {
for (int i = 0; i < list.size(); i++)
load(list.get(i));
order(list);
List goals = _goals;
if (goals != null) {
for (int i = 0; i < goals.size(); i++) {
if (loadedOrPending(goals.get(i))) {
onUpToDate();
break;
}
}
}
}
}
}
private static boolean replaces(Block block, long dependency) {
if (!Tick.contains(block.Dependencies, dependency)) {
if (Tick.peer(block.Tick) == Tick.peer(dependency))
if (Tick.time(block.Tick) >= Tick.time(dependency))
return true;
if (block.HappenedBefore != null)
if (Tick.happenedBefore(dependency, block.HappenedBefore))
return true;
}
return false;
}
private static boolean hasMissingDependencies(Block block, long[] missing) {
if (block.Dependencies != null)
for (int i = 0; i < block.Dependencies.length; i++)
if (!Tick.isNull(block.Dependencies[i]))
if (Tick.contains(missing, block.Dependencies[i]))
return true;
return false;
}
/*
* Don't block on goal with pending loads, cache might have received partial data that
* will be ignored for now but be in good state anyway.
*/
private final boolean loadedOrPending(long[] ticks) {
if (ticks != null)
for (int i = 0; i < ticks.length; i++)
if (!Tick.isNull(ticks[i]))
if (!Tick.happenedBefore(ticks[i], _loaded))
if (_pending == null || !_pending.containsKey(ticks[i]))
return false;
return true;
}
private final void load(Block block) {
_loaded = Tick.putMax(_loaded, block.Tick, true);
if (block.HappenedBefore != null)
for (int i = 0; i < block.HappenedBefore.length; i++)
if (!Tick.isNull(block.HappenedBefore[i]))
_loaded = Tick.putMax(_loaded, block.HappenedBefore[i], true);
block.Dependencies = null;
block.Next = null;
}
/*
* Ordering.
*/
private final void order(List list) {
Version[] versions = null;
for (int i = 0; i < list.size(); i++)
versions = order(list.get(i), versions);
if (versions != null) {
if (isLoaded())
commit(versions, list);
else {
for (int i = versions.length - 1; i >= 0; i--) {
if (versions[i] != null) {
if (versions[i].object() != this || !watcher().hasPendingAcks(this)) {
Version shared = versions[i].object().shared_();
shared.merge(shared, versions[i], true);
} else {
int todo;
System.out.println("skipping root");
}
}
}
}
}
}
private final Version[] order(Block block, Version[] versions) {
int index = binarySearch(_ordered, block.Tick, block.HappenedBefore);
boolean empty = false;
if (index < _ordered.size()) {
empty = true;
for (int i = index; i < _ordered.size(); i++)
empty &= mask(_ordered.get(i).Versions, block.Versions);
}
if (!empty) {
if (Debug.ENABLED)
Debug.assertion(block.Dependencies == null && block.Next == null);
_ordered.add(index, block);
maskPrevious(block.Versions, index);
if (versions == null)
versions = new Version[OpenMap.CAPACITY];
for (int i = block.Versions.length - 1; i >= 0; i--) {
if (block.Versions[i] != null) {
Version version = TransactionBase.getVersion(versions, block.Versions[i].object());
if (version == null) {
version = block.Versions[i].object().createVersion_();
versions = TransactionBase.putVersion(versions, version);
}
// Makes sure no races between masks & merges
version.deepCopy(block.Versions[i]);
}
}
}
return versions;
}
static int binarySearch(List list, long tick, long[] happenedBefores) {
int low = 0;
int high = list.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
Block midVal = list.get(mid);
int comp = compare(midVal.Tick, midVal.HappenedBefore, tick, happenedBefores);
if (comp < 0)
low = mid + 1;
else
high = mid - 1;
}
return low;
}
private static int compare(long a, long[] aHB, long b, long[] bHB) {
// TODO pre-compute in Peer somehow?
byte[] uidA = Peer.get(Tick.peer(a)).uid();
byte[] uidB = Peer.get(Tick.peer(b)).uid();
int compare = UID.compare(uidA, uidB);
if (compare == 0) {
if (Debug.ENABLED) {
Debug.assertion(Tick.peer(a) == Tick.peer(b));
Debug.assertion(Tick.time(a) != Tick.time(b));
}
return Tick.time(a) < Tick.time(b) ? -1 : 1;
}
if (compare > 0) {
if (bHB != null && Tick.happenedBefore(a, bHB))
return -compare;
return compare;
} else {
if (aHB != null && Tick.happenedBefore(b, aHB))
return -compare;
return compare;
}
}
static final class Block {
final long Tick;
final Version[] Versions;
final long[] HappenedBefore;
long[] Dependencies;
Block Next;
Block(long tick, Version[] versions, long[] happenedBefore, long[] dependencies) {
Tick = tick;
Versions = versions;
HappenedBefore = happenedBefore;
Dependencies = dependencies;
}
}
private final void commit(Version[] versions, List list) {
Snapshot previous = watcher().snapshot();
VersionMap watched = null;
for (;;) {
Snapshot snapshot = workspaceImpl().snapshot();
if (snapshot.last() == VersionMap.CLOSING)
break;
if (snapshot.last() != previous.last()) {
boolean empty = true;
VersionMap map;
for (;;) {
map = snapshot.last();
if (map.tryToAddWatchers(1)) {
if (Debug.ENABLED)
Helper.instance().addWatcher(map, this, snapshot, "mask");
break;
}
}
int index = Helper.getIndex(snapshot, previous.last());
for (int i = index + 1; i < snapshot.writes().length; i++)
empty &= mask(snapshot.writes()[i], versions);
if (watched != null) {
if (Debug.ENABLED)
Helper.instance().removeWatcher(watched, this, snapshot, "mask retry");
watched.removeWatchers(workspaceImpl(), 1, false, snapshot);
}
watched = map;
previous = snapshot;
if (empty)
break;
}
if (TransactionManager.load(workspaceImpl(), versions, snapshot.last(), this, list))
break;
}
if (watched != null) {
if (Debug.ENABLED)
Helper.instance().removeWatcher(watched, this, previous, "mask done");
watched.removeWatchers(workspaceImpl(), 1, false, previous);
}
}
void onLoad(Snapshot snapshot, List acks) {
}
private static boolean mask(Version[] masks, Version[] versions) {
boolean empty = true;
for (int i = versions.length - 1; i >= 0; i--) {
if (versions[i] != null) {
Version mask = TransactionBase.getVersion(masks, versions[i].object());
if (mask != null)
empty &= mask.mask(versions[i]);
else
empty = false;
}
}
return empty;
}
private final long[] maskPrevious(Version[] masks, int index) {
long[] removals = null;
for (int i = index - 1; i >= 0; i--) {
boolean empty = mask(masks, _ordered.get(i).Versions);
if (empty)
removals = Tick.add(removals, _ordered.remove(i).Tick);
}
return removals;
}
private final void onUpToDate() {
FutureWithCallbacks future = _uri.markLoaded(this);
if (future != null) {
_goals = null;
if (Debug.ENABLED)
Debug.assertion(current_() == null);
Snapshot snapshot;
for (;;) {
snapshot = workspaceImpl().snapshotWithoutClosing();
if (snapshot.last().tryToAddWatchers(1))
break;
}
if (Debug.ENABLED)
Helper.instance().addWatcher(snapshot.last(), this, snapshot, "Resource::onUpToDate()");
Object value = getFromPublicVersions(snapshot.writes());
value = value != NULL ? value : null;
if (Debug.ENABLED)
Helper.instance().removeWatcher(snapshot.last(), this, snapshot, "Resource::onUpToDate()");
snapshot.last().removeWatchers(workspaceImpl(), 1, false, null);
future.set(value);
if (watcher().removeHasPendingAcks(this, false)) {
if (Debug.ENABLED)
Debug.assertion(_pendingAcks.size() == 0);
watcher().writeChangesUntilUpToDate(this, value);
}
}
}
//
final void writeNewBlock(long tick, Version[] versions) {
if (Debug.ENABLED) {
Debug.assertion(isLoaded());
boolean ok = false;
for (int i = 0; i < versions.length; i++)
if (versions[i] != null)
ok = true;
Debug.assertion(ok);
}
long[] removals = maskPrevious(versions, _ordered.size());
for (int i = 0; i < _ordered.size(); i++)
watcher().writeDependency(_ordered.get(i).Tick);
long[] happenedBefore = new long[_loaded.length];
Platform.arraycopy(_loaded, 0, happenedBefore, 0, _loaded.length);
watcher().writeHappenedBefore(happenedBefore);
_ordered.add(new Block(tick, versions, happenedBefore, null));
_loaded = Tick.putMax(_loaded, tick, true);
Buff[] buffs = watcher().finishTick();
NewBlock block = new NewBlock();
Location[] caches = watcher().workspace().caches();
// Wait for ack of caches if any
if (caches != null)
block.PendingAcksBitSet = -1 >>> (32 - caches.length);
// And of origin if allowed to write
if (_permission == null || _permission == Permission.WRITE)
block.PendingAcksBitSet |= URIResolver.ORIGIN_BIT;
if (block.PendingAcksBitSet != 0)
addPendingAck(tick, block, buffs);
if (removals != null)
for (int i = 0; i < removals.length; i++)
if (!Tick.isNull(removals[i]))
removePendingAck(removals[i]);
Object key;
if (Debug.THREADS) {
for (int i = 0; i < buffs.length; i++)
ThreadAssert.exchangeGive(buffs, buffs[i]);
ThreadAssert.suspend(key = new Object());
}
Exception exception = onBlock(tick, buffs, removals);
if (Debug.THREADS) {
ThreadAssert.resume(key);
ThreadAssert.exchangeTake(buffs);
}
if (Debug.ENABLED)
Debug.assertion(exception == null);
for (int i = 0; i < buffs.length; i++)
buffs[i].recycle();
if (Stats.ENABLED)
Stats.Instance.BlockCreated.incrementAndGet();
}
Exception onBlock(long tick, Buff[] buffs, long[] removals) {
return _uri.onBlock(this, tick, buffs, removals, false, null, true);
}
private final void addPendingAck(long tick, NewBlock block, Buff[] buffs) {
Buff[] duplicates = new Buff[buffs.length];
for (int i = 0; i < duplicates.length; i++)
duplicates[i] = buffs[i].duplicate();
block.Buffs = duplicates;
//
if (_pendingAcks.size() == 0)
watcher().addHasPendingAcks(this);
_pendingAcks.put(tick, block);
}
private final void removePendingAck(long tick) {
NewBlock block = _pendingAcks.remove(tick);
if (block != null) {
for (int i = 0; i < block.Buffs.length; i++)
block.Buffs[i].recycle();
watcher().onBlockAck(Tick.time(tick));
if (_pendingAcks.size() == 0)
watcher().removeHasPendingAcks(this, true);
}
}
private static final class NewBlock {
Buff[] Buffs;
int PendingAcksBitSet;
}
/*
* Listeners.
*/
public final void addListener(ResourceListener listener) {
addListener(listener, workspaceImpl().callbackExecutor());
}
public final void addListener(ResourceListener listener, Executor executor) {
workspaceImpl().addListener(this, listener, executor);
}
public final void removeListener(ResourceListener listener) {
removeListener(listener, workspaceImpl().callbackExecutor());
}
public final void removeListener(ResourceListener listener, Executor executor) {
workspaceImpl().removeListener(this, listener, executor);
}
//
/**
* Trims ' ' and '/' at beginning and end.
*/
static String trim(String uri) {
while (uri.length() > 0) {
if (uri.charAt(0) == ' ')
uri = uri.substring(1);
else if (uri.charAt(0) == '/')
uri = uri.substring(1);
else if (uri.charAt(uri.length() - 1) == ' ')
uri = uri.substring(0, uri.length() - 1);
else if (uri.charAt(uri.length() - 1) == '/')
uri = uri.substring(0, uri.length() - 1);
else
break;
}
return uri;
}
@Override
public final int hashCode() {
// Final as used as key (Range, URI.getBlock)
if (Debug.ENABLED)
Helper.instance().disableEqualsOrHashCheck();
int value = super.hashCode();
if (Debug.ENABLED)
Helper.instance().enableEqualsOrHashCheck();
return value;
}
@Override
public final boolean equals(Object obj) {
if (Debug.ENABLED)
Helper.instance().disableEqualsOrHashCheck();
boolean value = super.equals(obj);
if (Debug.ENABLED)
Helper.instance().enableEqualsOrHashCheck();
return value;
}
@Override
public String toString() {
if (this == workspaceImpl().emptyResource())
return "Empty URI";
return _uri.toString();
}
//
@Override
final ResourceRead createRead() {
ResourceRead version = new ResourceRead();
version.setObject(this);
return version;
}
@Override
protected final ResourceVersion createVersion_() {
ResourceVersion version = new ResourceVersion();
version.setObject(this);
return version;
}
@Override
protected final int classId_() {
return BuiltInClass.RESOURCE_CLASS_ID;
}
//
static final class ResourceRead extends TObject.Version {
@Override
public boolean validAgainst(VersionMap map, Snapshot snapshot, int start, int stop) {
for (int i = start; i < stop; i++) {
TObject.Version write = TransactionBase.getVersion(snapshot.writes()[i], object());
if (write != null)
return false;
}
return true;
}
@Override
public void visit(org.objectfabric.Visitor visitor) {
visitor.visit(this);
}
}
static final class ResourceVersion extends TObject.Version {
private Object _value;
final Object getValue() {
return _value;
}
final void setValue(Object value) {
_value = value;
}
@Override
public TObject.Version merge(TObject.Version target, TObject.Version next, boolean threadPrivate) {
ResourceVersion source = (ResourceVersion) next;
ResourceVersion merged = this;
if (source._value != null)
merged._value = source._value;
return merged;
}
@Override
void deepCopy(Version source_) {
ResourceVersion source = (ResourceVersion) source_;
if (source._value != null)
_value = source._value;
}
@Override
public void visit(org.objectfabric.Visitor visitor) {
visitor.visit(this);
}
@Override
boolean mask(Version version) {
ResourceVersion uri = (ResourceVersion) version;
uri._value = null;
return true;
}
// Debug
@Override
void getContentForDebug(List list) {
if (!Debug.ENABLED)
throw new IllegalStateException();
list.add(_value);
}
@Override
boolean hasWritesForDebug() {
if (!Debug.ENABLED)
throw new IllegalStateException();
return true;
}
}
// Debug
final void assertIdle() {
if (!Debug.ENABLED)
throw new IllegalStateException();
Debug.assertion(range() == null);
Debug.assertion(id() == 0);
Debug.assertion(_pendingAcks.size() == 0);
}
}