All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.firebase.database.MutableData Maven / Gradle / Ivy

/*
 * Copyright 2017 Google Inc.
 *
 * 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 com.google.firebase.database;

import com.google.firebase.database.annotations.Nullable;
import com.google.firebase.database.core.Path;
import com.google.firebase.database.core.SnapshotHolder;
import com.google.firebase.database.core.ValidationPath;
import com.google.firebase.database.snapshot.ChildKey;
import com.google.firebase.database.snapshot.IndexedNode;
import com.google.firebase.database.snapshot.NamedNode;
import com.google.firebase.database.snapshot.Node;
import com.google.firebase.database.snapshot.NodeUtilities;
import com.google.firebase.database.snapshot.PriorityUtilities;
import com.google.firebase.database.utilities.Validation;
import com.google.firebase.database.utilities.encoding.CustomClassMapper;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * Instances of this class encapsulate the data and priority at a location. It is used in
 * transactions, and it is intended to be inspected and then updated to the desired data at that
 * location. 
*
* Note that changes made to a child MutableData instance will be visible to the parent and vice * versa. */ public class MutableData { private final SnapshotHolder holder; private final Path prefixPath; /** * Create a MutableData instance from a data node. * * @param node The data */ MutableData(Node node) { this(new SnapshotHolder(node), new Path("")); } private MutableData(SnapshotHolder holder, Path path) { this.holder = holder; prefixPath = path; ValidationPath.validateWithObject(prefixPath, getValue()); } Node getNode() { return holder.getNode(prefixPath); } /** Returns true if the data at this location has children, and false otherwise. */ public boolean hasChildren() { Node node = getNode(); return !node.isLeafNode() && !node.isEmpty(); } /** * @param path A relative path * @return True if data exists at the given path, otherwise false */ public boolean hasChild(String path) { return !getNode().getChild(new Path(path)).isEmpty(); } /** * Used to obtain a MutableData instance that encapsulates the data and priority at the given * relative path. * * @param path A relative path * @return An instance encapsulating the data and priority at the given path */ public MutableData child(String path) { Validation.validatePathString(path); return new MutableData(holder, prefixPath.child(new Path(path))); } /** * @return The number of immediate children at this location */ public long getChildrenCount() { return getNode().getChildCount(); } /** * Used to iterate over the immediate children at this location *
for (MutableData child : parent.getChildren()) { *
    ... *
} *
* * @return The immediate children at this location */ public Iterable getChildren() { Node node = getNode(); if (node.isEmpty() || node.isLeafNode()) { return new Iterable() { @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { return false; } @Override public MutableData next() { throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException("remove called on immutable collection"); } }; } }; } else { final Iterator iter = IndexedNode.from(node).iterator(); return new Iterable() { @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { return iter.hasNext(); } @Override public MutableData next() { NamedNode namedNode = iter.next(); return new MutableData(holder, prefixPath.child(namedNode.getName())); } @Override public void remove() { throw new UnsupportedOperationException("remove called on immutable collection"); } }; } }; } } /** * @return The key name of this location, or null if it is the top-most location */ public String getKey() { return prefixPath.getBack() != null ? prefixPath.getBack().asString() : null; } /** * getValue() returns the data contained in this instance as native types. The possible types * returned are: * *
    *
  • Boolean *
  • String *
  • Long *
  • Double *
  • Map<String, Object> *
  • List<Object> *
* *

This list is recursive; the possible types for Object in the above list is * given by the same list. These types correspond to the types available in JSON. * * @return The data contained in this instance as native types, or null if there is no data at * this location. */ @Nullable public Object getValue() { return getNode().getValue(); } /** * Due to the way that Java implements generics, it takes an extra step to get back a * properly-typed Collection. So, in the case where you want a List of Message * instances, you will need to do something like the following: * *


   *     GenericTypeIndicator<List<Message>> t =
   *         new GenericTypeIndicator<List<Message>>() {};
   *     List<Message> messages = mutableData.getValue(t);
   * 
* *

It is important to use a subclass of {@link GenericTypeIndicator}. See {@link * GenericTypeIndicator} for more details * * @param t A subclass of {@link GenericTypeIndicator} indicating the type of generic collection * to be returned. * @param The type to return. Implicitly defined from the {@link GenericTypeIndicator} passed * in * @return A properly typed collection, populated with the data from this instance, or null if * there is no data at this location. */ @Nullable public T getValue(GenericTypeIndicator t) { Object value = getNode().getValue(); return CustomClassMapper.convertToCustomClass(value, t); } /** * This method is used to marshall the data contained in this instance into a class of your * choosing. The class must fit 2 simple constraints: * *

    *
  1. The class must have a default constructor that takes no arguments *
  2. The class must define public getters for the properties to be assigned. Properties * without a public getter will be set to their default value when an instance is * deserialized *
* *

An example class might look like: * *


   *     class Message {
   *         private String author;
   *         private String text;
   *
   *         private Message() {}
   *
   *         public Message(String author, String text) {
   *             this.author = author;
   *             this.text = text;
   *         }
   *
   *         public String getAuthor() {
   *             return author;
   *         }
   *
   *         public String getText() {
   *             return text;
   *         }
   *     }
   *
   *
   *     // Later
   *     Message m = mutableData.getValue(Message.class);
   * 
* * @param valueType The class into which this data in this instance should be marshalled * @param The type to return. Implicitly defined from the class passed in * @return An instance of the class passed in, populated with the data from this instance, or null * if there is no data at this location. */ @Nullable public T getValue(Class valueType) { Object value = getNode().getValue(); return CustomClassMapper.convertToCustomClass(value, valueType); } /** * Set the data at this location to the given value. The native types accepted by this method for * the value correspond to the JSON types: * *
    *
  • Boolean *
  • Long *
  • Double *
  • Map<String, Object> *
  • List<Object> *
* *
*
* In addition, you can set instances of your own class into this location, provided they satisfy * the following constraints: * *
    *
  1. The class must have a default constructor that takes no arguments *
  2. The class must define public getters for the properties to be assigned. Properties * without a public getter will be set to their default value when an instance is * deserialized *
* *
*
* Generic collections of objects that satisfy the above constraints are also permitted, i.e. * Map<String, MyPOJO>, as well as null values. * *

Note that this overrides the priority, which must be set separately. * * @param value The value to set at this location */ public void setValue(Object value) throws DatabaseException { ValidationPath.validateWithObject(prefixPath, value); Object bouncedValue = CustomClassMapper.convertToPlainJavaTypes(value); Validation.validateWritableObject(bouncedValue); holder.update(prefixPath, NodeUtilities.NodeFromJSON(bouncedValue)); } /** * Gets the current priority at this location. The possible return types are: * *

    *
  • Double *
  • String *
* *

Note that null is allowed. * * @return The priority at this location as a native type */ public Object getPriority() { return getNode().getPriority().getValue(); } /** * Sets the priority at this location * * @param priority The desired priority */ public void setPriority(Object priority) { holder.update(prefixPath, getNode().updatePriority( PriorityUtilities.parsePriority(prefixPath, priority))); } @Override public boolean equals(Object o) { // Look for the same snapshot holder and the same prefix path return o instanceof MutableData && holder.equals(((MutableData) o).holder) && prefixPath.equals(((MutableData) o).prefixPath); } @Override public String toString() { ChildKey front = this.prefixPath.getFront(); return "MutableData { key = " + (front != null ? front.asString() : "") + ", value = " + this.holder.getRootNode().getValue(true) + " }"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy