Class DatabaseInstance<T extends DatabaseInstance<T>>

java.lang.Object
com.syzygy.events.database.DatabaseInstance<T>
All Implemented Interfaces:
Database.Dissolvable, Database.UpdateListener
Direct Known Subclasses:
DatabaseInstance.PropertyField.NullInstance, Event, EventAssociation, Facility, Image, Notification, User

@Dissolves public abstract class DatabaseInstance<T extends DatabaseInstance<T>> extends Object implements Database.UpdateListener, Database.Dissolvable
An instance of a database item which should be updated whenever the database is updated.@param T The class of the instance. TODO - Add error handling on instance exchanges TODO - change on instance load, if null, update subdelete
  • Field Details

    • referenceCount

      private int referenceCount
      The number of references to this Instance
    • isDereferenced

      private boolean isDereferenced
      If this instance is dereferenced.

      If the instance is no longer referenced, it is no longer maintained and updated by the database.

    • isInitialized

      private boolean isInitialized
      If the instance has been initialized with data
    • isInitializing

      private boolean isInitializing
      If the instance has been initialized with data
    • isDeleted

      private boolean isDeleted
      If the instance has been deleted
    • db

      protected final Database db
      The database
    • documentID

      private final String documentID
      A non-modifiable documentID that uniquely identifies the instance within its collection
    • snapshotListener

      private com.google.firebase.firestore.ListenerRegistration snapshotListener
      The snapshot listener that is associated with the database. This listener is called whenever the associated document is updated.
    • collection

      private final Database.Collections collection
      The database collection which the instance belongs to
    • properties

      protected final Map<String,DatabaseInstance.PropertyWrapper<?,?>> properties
      The set of properties that don't need to load
    • iproperties

      protected final List<String> iproperties
      The set of properties that need to load
    • updateListeners

      private final Set<Database.UpdateListener> updateListeners
      The set of listeners. Whenever the data of this instance changes, each listener is notified
      See Also:
    • initializationListeners

      private final Set<Database.InitializationListener<T extends DatabaseInstance<T>>> initializationListeners
      A set of listeners which are called when the instance is initialized
      See Also:
  • Constructor Details

  • Method Details

    • cast

      @Observes protected abstract T cast()
      Gets this instance casted to the generic type
      Returns:
      This as the generic type
    • getAssociatedImage

      @Nullable @Observes public final Image getAssociatedImage()
      Returns the image associated with this instance. Null if no image
    • getAssociatedImageLocName

      public String getAssociatedImageLocName()
      Returns the name that should be given to the image instance. If no image is associated, returns null
    • addListener

      public final void addListener(@Nullable Database.UpdateListener listener) throws IllegalStateException
      Adds a new update listener to this instance

      If the same listener is added twice, the listener will only be notified of an update once

      Parameters:
      listener - The listener
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      See Also:
    • removeListener

      public final boolean removeListener(@NonNull Database.UpdateListener listener)
      Removes a listener
      Parameters:
      listener - The listener
      Returns:
      If the set of listeners changed as a result
    • addInitializationListener

      @MustStir public final void addInitializationListener(@Nullable Database.InitializationListener<T> listener) throws IllegalStateException
      Adds a new initialization listener to this instance

      If the same listener is added twice, the listener will only be notified of an update once

      If the instance is already initialized, the listener is called immediately

      Parameters:
      listener - The listener
      Throws:
      IllegalStateException - if the instance is dereferenced
      See Also:
    • notifyUpdate

      protected final void notifyUpdate(Database.UpdateListener.Type type) throws IllegalStateException
      Notifies each listener
      Parameters:
      type - The type of update that occurred
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      See Also:
    • fetchWithoutThrow

      @MustStir private T fetchWithoutThrow()
      Increases the reference count of this instance.
      Returns:
      This instance casted to the generic type
    • fetch

      @MustStir public T fetch() throws IllegalStateException
      Increases the reference count of this instance.
      Returns:
      This instance casted to the generic type
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
    • fetch

      @MustStir public T fetch(Database.UpdateListener listener) throws IllegalStateException
      Increases the reference count of this instance and attaches the listener
      Parameters:
      listener - the update listener
      Returns:
      This instance casted to the generic type
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
    • dissolve

      @AutoStir @StirsDeep(what="Property Instances", when="No Longer Referenced") public void dissolve()
      Decreases the reference count of this instance
      Specified by:
      dissolve in interface Database.Dissolvable
    • dissolve

      @AutoStir @StirsDeep(what="Property Instances", when="No Longer Referenced") public void dissolve(Database.UpdateListener listener)
      Removes the listener from the instance and decreases the reference
      Parameters:
      listener - The listener
    • fullDissolve

      @AutoStir @StirsDeep(what="Property Instances") public void fullDissolve()
      Decreases the reference count to zero
    • isReferenced

      protected final boolean isReferenced()
      Checks if the reference count is positive
      Returns:
      true if one or more objects are referencing this instance
    • isLegalState

      public final boolean isLegalState()
      Returns if the object is in a legal state
      Returns:
      If the object is in a legal state
      See Also:
    • assertNotIllegalState

      protected final void assertNotIllegalState() throws IllegalStateException
      Checks if the instance is in a legal state The instance is in an illegal state if
      1. The instance is dereferenced isDereferenced or
      2. The instance has not been initialized with data isInitialized
      Throws:
      IllegalStateException - if the instance is in an illegal state
      See Also:
    • dereferenceInstance

      @StirsDeep(what="Property Instances") protected final void dereferenceInstance()
      Checks if the instance is dereferenced, in which case its tells the database to forget this instance and to remove it from cache. Also, clears all listeners.
    • subDereferenceInstance

      @StirsDeep(what="Property Instances") protected void subDereferenceInstance()
      Called when this instance is dereferenced. This function should dissolve any sub-instances
    • onUpdate

      @AutoStir(when="Instance is deleted and Property is not nullable") @StirsDeep(what="Property Instances", when="Instance is deleted and Property is not nullable") public <S extends DatabaseInstance<S>> void onUpdate(@Observes DatabaseInstance<S> instance, Database.UpdateListener.Type type)
      Called whenever any subinstance is updated. Notifies the listeners that a subupdate occurred unless an instance was deleted, then an actual update is notified
      Specified by:
      onUpdate in interface Database.UpdateListener
      Type Parameters:
      S - The type of the subinstance
      Parameters:
      instance - The subinstance
      type - The type of the update
      See Also:
    • onSubInstanceDelete

      @AutoStir(when="Property is not nullable") @StirsDeep(what="Property Instances", when="Property is not nullable") private void onSubInstanceDelete(@Observes DatabaseInstance<?> instance)
      Updates this instance appropriately when the subinstance is deleted

      Removes the instance. If the property cannot be null and is now null, this instance is deleted. Otherwise, notifies the listeners that an update occurred

      Parameters:
      instance - The subinstance
    • toString

      @NonNull public String toString() throws IllegalStateException
      Overrides:
      toString in class Object
      Throws:
      IllegalStateException
    • getDocumentID

      public final String getDocumentID()
      Returns the document ID
      Returns:
      the unique identifier of the instance within the collection
    • getDatabaseID

      final String getDatabaseID() throws IllegalStateException
      Returns a unique identifier for the instance within the full database
      Returns:
      The unique database identifier
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      See Also:
    • exchangeInstance

      @StirsDeep(what="Previous Instance") private <W extends DatabaseInstance<W>> void exchangeInstance(DatabaseInstance.PropertyWrapper<?,?> p, @Dilutes String newID, Consumer<Boolean> onComplete) throws IllegalArgumentException, ClassCastException
      Exchanges a instance property's value with the given id. Loads the new ID.
      Type Parameters:
      W - The type of the instance
      Parameters:
      p - The property to edit
      newID - The ID of the new instance
      onComplete - called on completion, true if no errors occurred, might not be called before return
      Throws:
      IllegalArgumentException - If the property is not an instance property
      ClassCastException - If the type does not match the properties type
    • exchangeInstance

      @AutoStir @StirsDeep(what="Previous Instance") private <W extends DatabaseInstance<W>> boolean exchangeInstance(DatabaseInstance.InstancePropertyWrapper<W> prop, @Dilutes W newInstance) throws ClassCastException
      Exchanges a instance property's instance and id with the given new instance. Fetches a reference to the newInstance
      Type Parameters:
      W - The type of the instance
      Parameters:
      prop - The property to edit
      newInstance - The new Instance
      Returns:
      If success full
      Throws:
      IllegalArgumentException - If the property is not an instance property
      ClassCastException - If the type does not match the properties type
    • modifyData

      @StirsDeep(what="Old Instances of Instance properties that are modified") protected final boolean modifyData(@Dilutes Map<String,Object> data, Consumer<Boolean> onComplete) throws IllegalArgumentException, ClassCastException
      Modifies the properties of the object given the data set. Calls all initialization listeners. Adds a snapshot listener to the document.

      Ignores if the field can edit

      Parameters:
      data - The property key-value set
      onComplete - called on completion. true if no errors. If none of the properties are setting an instance id, will be called before return.
      Returns:
      true if the instance was changed as a result
      Throws:
      IllegalArgumentException - if a key is not one of the available properties, or if the value is not valid
      ClassCastException - if a value does not match an properties type
    • setPropertyValue

      @StirsDeep(what="The previous instance", when="Modifying an instance property") public final boolean setPropertyValue(int resID, @Dilutes Object newValue, Consumer<Boolean> onComplete) throws IllegalArgumentException, IllegalStateException
      Edits the value of a property. If the property is an instance, loads the property and fetches the reference
      Parameters:
      resID - The res id of the property name
      newValue - the new value of the property
      onComplete - called on completion, true if no errors occurred. Will be called before return if setting a non loading property or if setting a loading property with an already instantiated instance
      Returns:
      if the property was changed
      Throws:
      IllegalArgumentException - if the property does not exist or if the property cannot be edited or the new value is invalid
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      ClassCastException - if the value type does not match the properties type
    • setPropertyInstance

      @StirsDeep(what="The previous instance") public final <W extends DatabaseInstance<W>> Boolean setPropertyInstance(int resID, @Nullable @Dilutes W instance) throws IllegalArgumentException, ClassCastException, IllegalStateException
      Edits the value of an instance property to the new instance. Fetches a reference to the new instance
      Parameters:
      resID - The res id of the property name
      instance - the instance to make the new value. This function will retrieve its own reference
      Returns:
      if the property was changed (null if error)
      Throws:
      IllegalArgumentException - if the property does not exist or if the property cannot be edited or the new value is invalid or the property is not an instance property
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      ClassCastException - if the instance type does not match the properties type
    • getPropertyValue

      @Observes public final Object getPropertyValue(int resID) throws IllegalArgumentException, ClassCastException
      Gets the value of a single property. In the case of instance properties, this returns the id
      Parameters:
      resID - The res id of the property name
      Returns:
      The value of the property as an Object
      Throws:
      IllegalArgumentException - if the property does not exist
      ClassCastException - if the generic is incorrect
    • getPropertyValueI

      @Observes public final <V> V getPropertyValueI(int resID) throws IllegalArgumentException, ClassCastException
      Gets the value of a single property. In the case of instance properties, this returns the id
      Type Parameters:
      V - The type of the property
      Parameters:
      resID - The res id of the property name
      Returns:
      The value of the property as an Object
      Throws:
      IllegalArgumentException - if the property does not exist
      ClassCastException - if the generic is incorrect
    • getPropertyInstance

      @Observes public final Object getPropertyInstance(int resID) throws IllegalArgumentException
      Gets the instance of an single instance property
      Parameters:
      resID - The res id of the property name
      Returns:
      The value of the property as an Object
      Throws:
      IllegalArgumentException - if the property does not exist or does not have instances
    • getPropertyInstanceI

      @Observes public final <W extends DatabaseInstance<W>> W getPropertyInstanceI(int resID) throws IllegalArgumentException, ClassCastException
      Gets the instance of an single instance property
      Parameters:
      resID - The res id of the property name
      Returns:
      The value of the property as an Object
      Throws:
      IllegalArgumentException - if the property does not exist or does not have instances
      ClassCastException - if the generic is incorrect
    • isPropertyValid

      public final boolean isPropertyValid(int resID, @Observes Object value)
      Test if the data is valid for the property
      Parameters:
      resID - The res id of the property name
      value - The value to test
      Returns:
      true if the value is valid
    • isPropertyValid

      public final boolean isPropertyValid(DatabaseInstance.PropertyWrapper<?,?> prop, @Observes Object value)
      Test if the data is valid for the property
      Parameters:
      prop - the property
      value - the value to test
      Returns:
      true if the value is valid
    • getFields

      public static DatabaseInstance.PropertyField<?,?>[] getFields(Database.Collections collection)
      Gets all properties of the given collection instance
      Returns:
      The property fields of the instance type
    • isDataValid

      public static Set<Integer> isDataValid(@Observes Map<Integer,Object> map, Database.Collections collection)
      Tests if the map is valid for this instance
      Parameters:
      map - The property resID-value map where resID is the res ID of the property name
      collection - The collection of the instance
      Returns:
      The set of all invalid ids
    • isDataValid

      static Set<Integer> isDataValid(@Observes Map<Integer,Object> map, DatabaseInstance.PropertyField<?,?>[] fields)
      Tests if the map is valid for this instance
      Parameters:
      map - The property resID-value map where resID is the res ID of the property name
      fields - The set of propertyFields to test against
      Returns:
      The set of all invalid ids
    • isPropertyEditable

      public final boolean isPropertyEditable(int resID)
      Checks if the property is editable
      Parameters:
      resID - the res id of the property name
      Returns:
      true if the property is present and editable
    • doesPropertyLoadInstance

      public final boolean doesPropertyLoadInstance(int resID)
      Checks if the property loads an instance
      Parameters:
      resID - the res id of the property name
      Returns:
      true if the property is present and loads an instance
    • initializeData

      @AutoStir(when="Error or not exists") @Stirred final void initializeData(@Dilutes Map<String,Object> data, boolean exists, Database.InitializationListener<T> onComplete) throws IllegalStateException
      Initializes the instance with data.

      This function sets the data. Then it sets the instance to a valid state. Then it notifies all initialization listeners and update listeners. Finally it adds the snapshot listener to the document

      If it does not exist, full dissolves and notifies all listeners with null

      Parameters:
      data - The property key-value mapping of the data
      exists - If the document exists in the database
      onComplete - Called when initialization is complete before the any other initialization listeners. This listener will not return a fetched instance, all other initialization listeners attached to the object will return a fetch instance.
      Throws:
      IllegalStateException - if the instance has already been initialized
    • subInitialize

      @Titrates(what="Sub instances") @Deprecated protected <X extends DatabaseInstance<X>> void subInitialize(Database.InitializationListener<T> listener, int count) throws IllegalArgumentException
      Deprecated.
      Modify data already does this, this is never called without modify data being called first
      This function is called after this instance's data has been initialized and set. This function should call the getter's for each sub-instance that needs to be stored. The listener should only be called once all sub-objects have been loaded. The listener's success should be set to false if any of the objects failed. Only sub-instances that are successfully loaded should be non-null when the listener is called, all failed should be set to null (they will be dereferenced automatically by their own initializers)
      Parameters:
      listener - The listener that should be called once all initialization is complete
      Throws:
      IllegalArgumentException - If the value is empty but the property is not nullable TODO does this X hack work?
    • updateDataFromDatabase

      @StirsDeep(what="Old Instances of Instance properties that are modified") final boolean updateDataFromDatabase(@Dilutes Map<String,Object> data, Consumer<Boolean> onComplete) throws IllegalStateException
      Modifies the properties of the object given the data set and notifies all listeners if the instance was changed.
      Parameters:
      data - The property key-value set
      onComplete - called once complete, true if no errors. If not modifying any instance ids, will be called before return
      Returns:
      true if the instance was changed as a result
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
    • updateDataFromMap

      @StirsDeep(what="Old Instances of Instance properties that are modified") public final Set<Integer> updateDataFromMap(@Dilutes Map<Integer,Object> data, @Nullable android.net.Uri image, String locName, Consumer<Boolean> onComplete) throws IllegalStateException, IllegalArgumentException, ClassCastException
      Validates the given data then creates the image and upon success updates the values and notifies listeners. Then updates the database. Updates the associated image with the new image
      Parameters:
      data - The property name id -> value mapping of all fields to be edited
      image - The image
      locName - The name of the location where the image is stored
      onComplete - called once complete, true if no errors. If not modifying any instance ids, will be called before return
      Returns:
      The set of invalid property ids upon validation
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      IllegalArgumentException - if a key is not one of the available properties to edit or a value is not valid
      ClassCastException - If a value's type does not match the property type
      See Also:
    • updateDataFromMap

      @StirsDeep(what="Old Instances of Instance properties that are modified") public final Set<Integer> updateDataFromMap(@Dilutes Map<Integer,Object> data, Consumer<Boolean> onComplete) throws IllegalStateException, IllegalArgumentException, ClassCastException
      Validates the given data then updates the values and notifies listeners. Then updates the database.
      Parameters:
      data - The property name id -> value mapping of all fields to be edited
      onComplete - called once complete, true if no errors. If not modifying any instance ids, will be called before return
      Returns:
      The set of invalid property ids upon validation
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      IllegalArgumentException - if a key is not one of the available properties to edit or a value is not valid
      ClassCastException - If a value's type does not match the property type
    • setAssociatedImage

      @StirsDeep(what="The previous Image") public void setAssociatedImage(@Nullable android.net.Uri image, Consumer<Boolean> onComplete) throws UnsupportedOperationException
      Sets the associated Image instance. This function will create a new reference to the instance.
      Parameters:
      image - The new instance
      onComplete - called on completion with if the update was successful
      Throws:
      UnsupportedOperationException - if the instance type does not have an editable associated image
    • getData

      @Observes protected final Map<String,Object> getData() throws IllegalStateException
      Returns the data of the instance
      Returns:
      The property key-value map of the instance
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
    • processUpdate

      protected final void processUpdate() throws IllegalStateException
      Notifies all listeners that an update has occurred and modifies the database to match
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      See Also:
    • deleteInstance

      @AutoStir @StirsDeep(what="Sub Instances") public void deleteInstance(int deletionType, Consumer<Boolean> listener)
      Dereferences the instance. Then cascade deletes all cascading properties. Then notifies all listeners that the instance was deleted.
      Parameters:
      deletionType - The DatabaseInstance.DeletionType reason this instance is being deleted
      listener - called on completion of deletion, returns false if one or more errors occured
      See Also:
    • requiredFirstDelete

      protected void requiredFirstDelete(int deletionType, Consumer<Boolean> listener)
      Deletes any sub objects that are not instances
      Parameters:
      deletionType - The DatabaseInstance.DeletionType reason this instance is being deleted
      listener - Called before the instance and sub instances are deleted; they are only deleted if this returns true
    • deleteSubInstances

      @StirsDeep(what="Sub instances") private void deleteSubInstances(int deletionType, Consumer<Boolean> listener)
      Deletes all subinstances that are cascaded
      Parameters:
      deletionType - The DatabaseInstance.DeletionType reason this instance is being deleted
      listener - called on completion of deletion
    • subInstanceCascadeDeleteQuery

      protected abstract List<android.util.Pair<com.google.firebase.firestore.Query,Database.Collections>> subInstanceCascadeDeleteQuery()
      Returns:
      A list of all querries that should be run whose instances will be deleted when this instance is deleted
    • getCollection

      public final Database.Collections getCollection()
      Returns the collection that this instance is apart of
      Returns:
      the Database.Collections
    • getDocumentReference

      public final com.google.firebase.firestore.DocumentReference getDocumentReference()
      Returns the DocumentReference associated with this instance

      If the document did not exist, a new document is created

      Returns:
      the database reference to this instance
      See Also:
    • getCollectionReference

      final com.google.firebase.firestore.CollectionReference getCollectionReference() throws IllegalStateException
      Returns the CollectionReference associated with this instance
      Returns:
      the database reference to the collection containing this instance
      Throws:
      IllegalStateException - if the instance is in an illegal state assertNotIllegalState()
      See Also: