/*
* Copyright 2000-2018 Vaadin Ltd.
*
* 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.vaadin.client;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;
import com.vaadin.server.PaintTarget;
import com.vaadin.ui.Component;
/**
* When a component is updated, it's client side widget's
* {@link ComponentConnector#updateFromUIDL(UIDL, ApplicationConnection)
* updateFromUIDL()} will be called with the updated ("changes") UIDL received
* from the server.
*
* UIDL is hierarchical, and there are a few methods to retrieve the children,
* {@link #getChildCount()}, {@link #iterator()} {@link #getChildString(int)},
* {@link #getChildUIDL(int)}.
*
*
* It can be helpful to keep in mind that UIDL was originally modeled in XML, so
* it's structure is very XML -like. For instance, the first to children in the
* underlying UIDL representation will contain the "tag" name and attributes,
* but will be skipped by the methods mentioned above.
*
*/
public final class UIDL extends JavaScriptObject implements Iterable {
protected UIDL() {
}
/**
* Shorthand for getting the attribute named "id", which for Paintables is
* the essential paintableId which binds the server side component to the
* client side widget.
*
* @return the value of the id attribute, if available
*/
public String getId() {
return getStringAttribute("id");
}
/**
* Gets the name of this UIDL section, as created with
* {@link PaintTarget#startTag(String) PaintTarget.startTag()} in the
* server-side {@link Component#paint(PaintTarget) Component.paint()} or
* (usually)
* {@link com.vaadin.ui.AbstractComponent#paintContent(PaintTarget)
* AbstractComponent.paintContent()}. Note that if the UIDL corresponds to a
* Paintable, a component identifier will be returned instead - this is used
* internally and is not needed within
* {@link ComponentConnector#updateFromUIDL(UIDL, ApplicationConnection)
* updateFromUIDL()}.
*
* @return the name for this section
*/
public native String getTag()
/*-{
return this[0];
}-*/;
private native ValueMap attr()
/*-{
return this[1];
}-*/;
private native ValueMap var()
/*-{
return this[1]["v"];
}-*/;
private native boolean hasVariables()
/*-{
return Boolean(this[1]["v"]);
}-*/;
/**
* Gets the named attribute as a String.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public String getStringAttribute(String name) {
return attr().getString(name);
}
/**
* Gets the names of the attributes available.
*
* @return the names of available attributes
*/
public Set getAttributeNames() {
Set keySet = attr().getKeySet();
keySet.remove("v");
return keySet;
}
/**
* Gets the names of variables available.
*
* @return the names of available variables
*/
public Set getVariableNames() {
if (!hasVariables()) {
return new HashSet<>();
} else {
Set keySet = var().getKeySet();
return keySet;
}
}
/**
* Gets the named attribute as an int.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public int getIntAttribute(String name) {
return attr().getInt(name);
}
/**
* Gets the named attribute as a long.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public long getLongAttribute(String name) {
return (long) attr().getRawNumber(name);
}
/**
* Gets the named attribute as a float.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public float getFloatAttribute(String name) {
return (float) attr().getRawNumber(name);
}
/**
* Gets the named attribute as a double.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public double getDoubleAttribute(String name) {
return attr().getRawNumber(name);
}
/**
* Gets the named attribute as a boolean.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public boolean getBooleanAttribute(String name) {
return attr().getBoolean(name);
}
/**
* Gets the named attribute as a Map of named values (key/value pairs).
*
* @param name
* the name of the attribute to get
* @return the attribute Map
*/
public ValueMap getMapAttribute(String name) {
return attr().getValueMap(name);
}
/**
* Gets the named attribute as an array of Strings.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public String[] getStringArrayAttribute(String name) {
return attr().getStringArray(name);
}
/**
* Gets the named attribute as an int array.
*
* @param name
* the name of the attribute to get
* @return the attribute value
*/
public int[] getIntArrayAttribute(final String name) {
return attr().getIntArray(name);
}
/**
* Get attributes value as string whatever the type is
*
* @param name
* @return string presentation of attribute
*/
native String getAttribute(String name)
/*-{
return '' + this[1][name];
}-*/;
native String getVariable(String name)
/*-{
return '' + this[1]['v'][name];
}-*/;
/**
* Indicates whether or not the named attribute is available.
*
* @param name
* the name of the attribute to check
* @return true if the attribute is available, false otherwise
*/
public boolean hasAttribute(final String name) {
return attr().containsKey(name);
}
/**
* Gets the UIDL for the child at the given index.
*
* @param i
* the index of the child to get
* @return the UIDL of the child if it exists
*/
public native UIDL getChildUIDL(int i)
/*-{
return this[i + 2];
}-*/;
/**
* Gets the child at the given index as a String.
*
* @param i
* the index of the child to get
* @return the String representation of the child if it exists
*/
public native String getChildString(int i)
/*-{
return this[i + 2];
}-*/;
private native XML getChildXML(int index)
/*-{
return this[index + 2];
}-*/;
/**
* Gets an iterator that can be used to iterate trough the children of this
* UIDL.
*
* The Object returned by next()
will be appropriately typed -
* if it's UIDL, {@link #getTag()} can be used to check which section is in
* question.
*
*
* The basic use case is to iterate over the children of an UIDL update, and
* update the appropriate part of the widget for each child encountered, e.g
* if getTag()
returns "color", one would update the widgets
* color to reflect the value of the "color" section.
*
*
* @return an iterator for iterating over UIDL children
* @deprecated As of 8.2, please use {@link #iterator()} instead
*/
@Deprecated
public Iterator getChildIterator() {
return iterator();
}
/**
* Gets an iterator that can be used to iterate trough the children of this
* UIDL.
*
* The Object returned by next()
will be appropriately typed -
* if it's UIDL, {@link #getTag()} can be used to check which section is in
* question.
*
*
* The basic use case is to iterate over the children of an UIDL update, and
* update the appropriate part of the widget for each child encountered, e.g
* if getTag()
returns "color", one would update the widgets
* color to reflect the value of the "color" section.
*
*
* @return an iterator for iterating over UIDL children
* @since 8.2
*/
@Override
public Iterator iterator() {
return new Iterator() {
int index = -1;
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public Object next() {
if (hasNext()) {
int typeOfChild = typeOfChild(++index);
switch (typeOfChild) {
case CHILD_TYPE_UIDL:
UIDL childUIDL = getChildUIDL(index);
return childUIDL;
case CHILD_TYPE_STRING:
return getChildString(index);
case CHILD_TYPE_XML:
return getChildXML(index);
default:
throw new IllegalStateException("Illegal child in tag "
+ getTag() + " at index " + index);
}
}
return null;
}
@Override
public boolean hasNext() {
int count = getChildCount();
return count > index + 1;
}
};
}
private static final int CHILD_TYPE_STRING = 0;
private static final int CHILD_TYPE_UIDL = 1;
private static final int CHILD_TYPE_XML = 2;
private native int typeOfChild(int index)
/*-{
var t = typeof this[index + 2];
if (t == "object") {
if (typeof(t.length) == "number") {
return 1;
}
return 2;
}
if (t == "string") {
return 0;
}
return -1;
}-*/;
/**
* @deprecated
*/
@Deprecated
public String getChildrenAsXML() {
return toString();
}
/**
* Checks if the named variable is available.
*
* @param name
* the name of the variable desired
* @return true if the variable exists, false otherwise
*/
public boolean hasVariable(String name) {
return hasVariables() && var().containsKey(name);
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public String getStringVariable(String name) {
return var().getString(name);
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public int getIntVariable(String name) {
return var().getInt(name);
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public long getLongVariable(String name) {
return (long) var().getRawNumber(name);
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public float getFloatVariable(String name) {
return (float) var().getRawNumber(name);
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public double getDoubleVariable(String name) {
return var().getRawNumber(name);
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public boolean getBooleanVariable(String name) {
return var().getBoolean(name);
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public String[] getStringArrayVariable(String name) {
return var().getStringArray(name);
}
/**
* Gets the value of the named String[] variable as a Set of Strings.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public Set getStringArrayVariableAsSet(final String name) {
final HashSet s = new HashSet<>();
JsArrayString a = var().getJSStringArray(name);
for (int i = 0; i < a.length(); i++) {
s.add(a.get(i));
}
return s;
}
/**
* Gets the value of the named variable.
*
* @param name
* the name of the variable
* @return the value of the variable
*/
public int[] getIntArrayVariable(String name) {
return var().getIntArray(name);
}
/**
* @deprecated should not be used anymore
*/
@Deprecated
public static final class XML extends JavaScriptObject {
protected XML() {
}
public native String getXMLAsString()
/*-{
var buf = new Array();
var self = this;
for (j in self) {
buf.push("<");
buf.push(j);
buf.push(">");
buf.push(self[j]);
buf.push("");
buf.push(j);
buf.push(">");
}
return buf.join("");
}-*/;
}
/**
* Returns the number of children.
*
* @return the number of children
*/
public native int getChildCount()
/*-{
return this.length - 2;
}-*/;
native boolean isMapAttribute(String name)
/*-{
return typeof this[1][name] == "object";
}-*/;
/**
* Gets the Paintable with the id found in the named attributes's value.
*
* @param name
* the name of the attribute
* @return the Paintable referenced by the attribute, if it exists
*/
public ServerConnector getPaintableAttribute(String name,
ApplicationConnection connection) {
return ConnectorMap.get(connection)
.getConnector(getStringAttribute(name));
}
/**
* Gets the Paintable with the id found in the named variable's value.
*
* @param name
* the name of the variable
* @return the Paintable referenced by the variable, if it exists
*/
public ServerConnector getPaintableVariable(String name,
ApplicationConnection connection) {
return ConnectorMap.get(connection)
.getConnector(getStringVariable(name));
}
/**
* Returns the child UIDL by its name. If several child nodes exist with the
* given name, the first child UIDL will be returned.
*
* @param tagName
* @return the child UIDL or null if child wit given name was not found
*/
public UIDL getChildByTagName(String tagName) {
for (Object next : this) {
if (next instanceof UIDL) {
UIDL childUIDL = (UIDL) next;
if (childUIDL.getTag().equals(tagName)) {
return childUIDL;
}
}
}
return null;
}
}