package com.sap.runlet.abstractinterpreter.repository;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.EObject;
import com.sap.runlet.abstractinterpreter.objects.ClassTypedObject;
import com.sap.runlet.abstractinterpreter.objects.EntityObject;
import com.sap.runlet.abstractinterpreter.objects.Link;
/**
* <h2>Overview</h2>
*
* Represents a durable, persistent and fully versioned repository of objects, values and links.
* Entity objects can be saved ("committed") to this repository. According to a strategy explained
* below, the links and a subset of the linked objects will be saved to the repository, too. Values
* are only saved as per saving an entity or by saving a link between two values which may happen
* independently of saving an entity.
* <p>
*
* The repository manages a persistent <em>extent</em> for each
* {@link SapClass#isValueType() entity class}, and it manages a persistent extent for each
* {@link Association association}. Entity objects and links can therefore be added to or removed
* from their respective extent. The most simple form of query is that of retrieving an entire
* extent of either a class of an association. The next simple query is that of navigating from one
* object or a set of objects across the links stored in a certain association extent. Values are
* part of the ends of the links referring to them. Their classes don't have extents of their own,
* and a value does not have a life cycle of its own. It conceptually always exists.
* <p>
*
* It is possible to determine the existence of an entity in the repository. This can, e.g., be
* useful to find out if the entity has been deleted from the repository or if it hasn't been stored
* yet.
* <p>
*
* The strategy for saving an entity to the repository will be that it, all its direct and
* transitive composite children as well as all links attaching to any of these objects, and all
* value objects associated with any of these objects that are relevant for the saved objects'
* equality will be saved into the repository together with the entity. As a result, entities that
* used to be composite children but that are no longer are considered deleted unless they are
* explicitly saved again (which may happen in the same call because multiple entities can be passed
* and saved at once). The same deletion strategy applies for all links: links that used to be
* attached to any of the objects written by the save and that are no longer attached are considered
* deleted. In other words: the composition tree with all inner and external links is considered
* authoritative regarding both the existence of composed entities as well as the existence of links
* within and attached to the composition tree.
* <p>
*
* It is possible to <em>delete</em> an entity. If the entity had a composite parent, the result
* is the same as if the link to the composite parent had been deleted and the composite parent then
* had been saved. This, according to the strategy defined above, would also delete the entity
* together with all entities contained by it as well as all links attached to any entity within
* this composition subtree. However, if the entity to be deleted does not have a composite parent
* or it is impractical or infeasible for some reason to save the parent, an explicit delete can be
* performed on an entity with the same effect: it, all its composite children and all links
* attaching to thie composition tree will be deleted.
* <p>
*
* <h2>Versioning and Snapshots</h2>
*
* A repository maintains state as a set of <em>snapshots</em> (versions). At any one time, the
* repository identifies one snapshot as the <em>current default snapshot</em>. Whenever the
* repository is read without specifying a particular snapshot, this will be the snapshot based on
* whose contents the read will be satisfied. Access to the repository may also query the repository
* state as it was at a certain snapshot different from the current default snapshot. Imagine the
* entire set-up as a versioning repository where a <tt>commit</tt> produces a new snapshot that
* gets a unique snapshot ID, such as, e.g., in the <a
* href="http://subversion.tigris.org/design.html#server.fs.struct.bubble-up">Subversion (SVN)</a>
* versioning system and similar to the <a
* href="http://eagain.net/articles/git-for-computer-scientists/">git</a> versioning system.
* <p>
*
* Whenever a change is committed to the repository, it creates a new, unique snapshot (or
* repository revision) (see {@link Snapshot}. This means in particular that deleting an object or
* a link does not alter previous snapshots where those will continue to exist. The repository
* can tell the contents of all the extents it manages at each of the snapshots it ever committed.
* (An exception to this rule may be the forced physical deletion of data as, e.g., for legal or
* security reasons. Special permissions and auditing should be required for this operation.) The
* difference between any two snapshots is called a {@link ChangeSet} and can be determined by the
* repository from any pair of snapshots. Vice versa, it is possible to create a new snapshot given
* an existing snapshot and a {@link ChangeSet} that describes the changes transitioning from the
* existing snapshot to the new one.
* <p>
*
* <h2>Future extensions</h2>
*
* <ul>
*
* <li>Changes can be based on data coming from existing snapshots. In order to avoid
* harmful "last writer wins" type of conflicts, snapshots should know their predecessors,
* and a snapshot should not be promoted to the current default snapshot if the current default
* snapshot so far is already a successor of the predecessors of the new snapshot that contains
* conflicting changes. For this, we need a definition of what is a conflict between two
* {@link ChangeSet}s, and we need a notion of "predecessor" on {@link Snapshot}s.</li>
*
* <li>The repository shall support queries by value and association which will result in a set of
* entities that are associated with the given value through the association given. For example,
* this will allow querying persons by name.</li>
*
* </ul>
*
* @author Axel Uhl (D043530)
*
*/
public interface Repository<LinkMetaObject extends EObject, LinkEndMetaObject extends EObject, MetaClass extends EObject,
TypeUsage extends EObject, ClassUsage extends TypeUsage> {
/**
* @return the "current" or "default" snapshot available at the time of calling. If no means of
* locking or synchronization are available or used, it may of course happen that
* between asking this snapshot and reading from this snapshot a newer snapshot that
* {@link Snapshot#succeeds(Snapshot) succeeds} the snapshot returned may have been
* committed to this repository. To avoid this, use snapshot-less access signatures
* which will default to using the latest snapshot available at that time.
*/
Snapshot getCurrent();
/**
* @return a "tag" for the trunk of this repository, not yet resolved; when the snapshot
* identifier returned is {@link #resolve resolved}, it points to the snapshot returned
* by {@link #getCurrent} at the point in time of calling {@link #resolve}. Of course,
* subsequent calls to {@link #apply} can progress {@link #getCurrent} while the tag
* returned by this method still points to a previous
*/
SnapshotIdentifier getTrunkIdentifier();
/**
* Fetches all links stored in this repository at the {@link #getCurrent() current snapshot}
* that are instance of the <tt>association</tt>. More formally, those links are all
* guaranteed to have {@link Link#getAssociation()} <tt>== association</tt>.
* <p>
*
* TODO make some guarantees about the ordering in case the association has an ordered end<p>
*
* @param fromSnapshot
* a non-<tt>null</tt> object; read from the snapshot specified; in case the
* {@link SnapshotIdentifier#getSnapshot()} returns <tt>null</tt>, the
* identifier is {@link #resolve(SnapshotIdentifier) resolved} first.
* @return all links stored in the snapshot identified by <tt>fromSnapshot</tt> that
* are instance of <tt>association</tt>. The links returned will as their
* {@link RepositoryObject#getOrigin() snapshot} have <tt>fromSnapshot</tt>.
*/
Collection<Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> allLinks(LinkMetaObject association, SnapshotIdentifier fromSnapshot);
/**
* Returns all entities that at the snapshot determined by <tt>fromSnapshot</tt> are stored in
* this repository and that are instance of class <tt>clazz</tt>. More formally, for all
* objects <tt>o</tt> returned, <tt>o.getType().getClazz() == clazz</tt> holds. If
* <tt>fromSnapshot</tt> names a <tt>null</tt> snapshot, the identifier is
* {@link #resolve(SnapshotIdentifier) resolved} to bind it to a valid {@link Snapshot}.
*
* @param clazz
* expected to have {@link SapClass#isValueType()}<tt>==false</tt>.
* @param fromSnapshot
* a non-<tt>null</tt> object; read from the snapshot specified or
* {@link #resolve(SnapshotIdentifier) resolve} in case the
* {@link SnapshotIdentifier#getSnapshot()} returns <tt>null</tt>, then read
* from that snapshot
* @return all entity objects stored in the snapshot identified by <tt>fromSnapshot</tt> that
* are instance of <tt>clazz</tt>. The objects returned will as their
* {@link RepositoryObject#getOrigin() snapshot} have <tt>fromSnapshot</tt>. This
* call always returns a non-<tt>null</tt> collection which may be empty if no
* objects of the class requested are found in the snapshot identified by
* <tt>fromSnapshot</tt or if that snapshot is not found.
*/
Collection<EntityObject<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> all(MetaClass clazz, SnapshotIdentifier fromSnapshot);
/**
* Tells if this repository knows the <tt>snapshot</tt>. Return <tt>true</tt> if the
* <tt>snapshot</tt> equals one that has been returned by a call to
* {@link #commit(Collection, RiverInterpreter)} on this repository.
*/
boolean has(Snapshot snapshot);
/**
* Tells if snapshot <tt>a</tt> is a direct or indirect predecessor of snapshot <tt>b</tt>.
* The relation implemented by this method is not reflexive, meaning that
* <tt>precedes(s, s)</tt> always returns <tt>false</tt>. There may be uncomparable
* snapshots where none is predecessor of the other.
*/
boolean precedes(Snapshot a, Snapshot b);
/**
* Tests if the two snapshots are equal. Snapshots are equal iff
* they have the same set of {@link EntityObject}s and {@link Link}s.
* identity.
* @param s1
* @param s2
* @return <code>true</code> iff the s1 and s2 are equal, <code>false</code> otherwise.
*/
boolean testSnapshotEquality(Snapshot s1, Snapshot s2);
/**
* Determines those snapshots held by this repository that are immediate predecessors of
* <tt>snapshot</tt>. If no such snapshot exists, an empty collection is returned.
*/
Set<Snapshot> getImmediatePredecessors(Snapshot snapshot);
/**
* Tells if snapshot <tt>a</tt> is a <em>direct</em> predecessor of <tt>b</tt>. If this
* is the case, no other snapshot <tt>c</tt> exists in this repository for which
* <tt>precedes(a, c) and precedes(c, b)</tt> holds.
*/
boolean directlyPrecedes(Snapshot a, Snapshot b);
/**
* Creates a new snapshot by applying a changeset. No implicit completions and navigations take
* place. All additions and deletions have to be specified by the caller. In particular all
* composite children of an entity that were created or deleted need to be passed in
* <tt>entities</tt> or <tt>deletedEntities</tt>, respectively.
* <p>
*
* All other data will remain as would be returned by a query to the snapshot identified by
* <tt>applyTo</tt>.<p>
*
* Callers should be aware that <tt>applyTo</tt> may be a tag that, when resolved again after
* this method returns, resolves to the snapshot returned by this method. If the snapshot
* returned by this method is not an {@link #directlyPrecedes(Snapshot, Snapshot) immediate successor}
* of the snapshot <tt>applyTo</tt> was bound to at the time of this call, other change sets
* might have been merged in between, and the in-memory contents pertinent to the
* <tt>applyTo</tt> identifier may be invalid now.
*
* @param applyTo
* a non-<tt>null</tt> object that identifies the snapshot to which to apply
* the changeset. If {@link SnapshotIdentifier#getSnapshot()} returns <tt>null</tt>,
* the identifier is {@link #resolve(SnapshotIdentifier) resolved} first.
*
* @return the snapshot created by applying the change set. May---depending on <tt>applyTo</tt>'s
* type---be the same as the one identified by <tt>applyTo</tt>, either if the
* <tt>changeset</tt> was {@link ChangeSet#isEmpty() empty} and therefore no
* new snapshot was produced at all, or because <tt>applyTo</tt> identifies the head
* of the trunk or some branch and therefore automatically moves to the new snapshot
* if {@link #resolve(SnapshotIdentifier) resolved} again.
*/
Snapshot apply(ChangeSet<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage> changeset, SnapshotIdentifier applyTo);
/**
* Computes the common ancestor of the two snapshots and then merges the changes performed
* on the branch that led from the common ancestor to the <tt>fromBranch</tt> snapshot into
* the trunk. This will produce a new successor snapshot that has two predecessors,
* <tt>fromBranch</tt> and <tt>intoTrunk</tt>.
*/
Snapshot merge(Snapshot fromBranch, Snapshot intoTrunk);
/**
* Looks up all links from <tt>from</tt> towards the association end identified by
* <tt>otherEnd</tt> within {@link ClassTypedObject#getOrigin() from's snapshot}. All links
* returned have <tt>from</tt> on the end opposite of <tt>otherEnd</tt>. The collection
* returned is always non-<tt>null</tt>.
*
* @param from
* expected to have a non-<tt>null</tt>
* {@link RepositoryObject#getOrigin() snapshot identifier}. The contents of the
* snapshot identified by it is used to compute the navigation results.
* @return a non-null but possibly empty link collection; those are all links in <tt>from</tt>'s
* snapshot for which <tt>from</tt> is on end <tt>otherEnd.otherEnd()</tt>. The
* links returned will all have the same snapshot identifier as <tt>from</tt>.
*/
Collection<Link<LinkMetaObject, LinkEndMetaObject, MetaClass, TypeUsage, ClassUsage>> loadLinks(
ClassTypedObject<LinkEndMetaObject, TypeUsage, ClassUsage> from,
LinkEndMetaObject otherEnd);
/**
* Binds the snapshot identifier to a particular snapshot. Afterwards,
* {@link SnapshotIdentifier#getSnapshot()} on the <tt>tag</tt> returns the snapshot to which
* the identifier has been bound by this call.<p>
*/
void resolve(SnapshotIdentifier tag);
/**
* Determines a maximal set of snapshots such that for each of them there is no successor
* in this repository that has a {@link Snapshot#when() timestamp} before
* <tt>fromLastConcurrentSnapshotsBefore</tt>.
*/
Set<SnapshotIdentifier> getLastSnapshotsBeforeOrAt(Date fromLastConcurrentSnapshotsBefore);
/**
* Determines a snapshot <tt>s</tt> such that for all <tt>t</tt> from <tt>snapshots</tt>
* the condition <tt>this.precedes(s, t) || s.equals(t)</tt> holds. In particular, if the
* <tt>snapshots</tt> set contains only one snapshot, that snapshot will be used as <tt>s</tt>.
* If the elements in <tt>snapshots</tt> are in a predecessor relation with each other,
* the "first" of them (according to the {@link #precedes} relation) is used as <tt>s</tt>.
* The method returns the paths from <tt>s</tt> to the respective elements from <tt>snapshots</tt>
* or <tt>null</tt> if no common ancestor snapshot <tt>s</tt> can be found.
*
* @param snapshots a non-<tt>null</tt> but possibly empty set of snapshots
*/
List<List<Snapshot>> getPathsFromLastCommonAncestor(Snapshot... snapshots);
/**
* Returns the set of all known snapshots.
* @return Set of all known {@link Snapshot} instances.
*/
Set<Snapshot> getAllSnapshots();
Snapshot resolveTag(Tag tag);
Snapshot resolveLastBeforeDateOnBranch(Date timestamp, Tag branchIdentifier);
}