Java tutorial
/* * Copyright 2008 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.gwt.dom.client; import java.util.AbstractList; import java.util.List; import java.util.stream.Stream; import com.google.common.base.Preconditions; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JavascriptObjectEquivalent; /** * The Node interface is the primary datatype for the entire Document Object * Model. It represents a single node in the document tree. While all objects * implementing the Node interface expose methods for dealing with children, not * all objects implementing the Node interface may have children. */ public abstract class Node implements JavascriptObjectEquivalent, DomNode { /** * The node is an {@link Element}. */ public static final short ELEMENT_NODE = 1; /** * The node is a {@link Text} node. */ public static final short TEXT_NODE = 3; /** * The node is a {@link Document}. */ public static final short DOCUMENT_NODE = 9; /** * Assert that the given {@link JavaScriptObject} is a DOM node and * automatically typecast it. */ public static Node as(JavascriptObjectEquivalent o) { if (o instanceof JavaScriptObject) { JavaScriptObject jso = (JavaScriptObject) o; assert isJso(jso); return LocalDom.nodeFor(jso); } else { return (Node) o; } } public static boolean is(JavascriptObjectEquivalent o) { if (o instanceof JavaScriptObject) { JavaScriptObject jso = (JavaScriptObject) o; return isJso(jso); } return o instanceof Node; } /** * Determines whether the given {@link JavaScriptObject} is a DOM node. A * <code>null</code> object will cause this method to return * <code>false</code>. The try catch is needed for the firefox permission * error: "Permission denied to access property 'nodeType'" */ private static native boolean isJso(JavaScriptObject o) /*-{ try { return (!!o) && (!!o.nodeType); } catch (e) { return false; } }-*/; private int resolvedEventId; protected Node() { } @Override public <T extends Node> T appendChild(T newChild) { validateInsert(newChild); doPreTreeResolution(newChild); T node = local().appendChild(newChild); remote().appendChild(newChild); return node; } @Override public void callMethod(String methodName) { DomNodeStatic.callMethod(this, methodName); } @Override public abstract <T extends JavascriptObjectEquivalent> T cast(); @Override public Node cloneNode(boolean deep) { // FIXME - maybe - remote should probably always be resolved (so maybe // ok) return local().cloneNode(deep); } @Override public Node getChild(int index) { return DomNodeStatic.getChild(this, index); } @Override public int getChildCount() { return DomNodeStatic.getChildCount(this); } @Override public NodeList<Node> getChildNodes() { return local().getChildNodes(); } @Override public Node getFirstChild() { return local().getFirstChild(); } @Override public Node getLastChild() { return local().getLastChild(); } @Override public Node getNextSibling() { return local().getNextSibling(); } @Override public String getNodeName() { return local().getNodeName(); } @Override public short getNodeType() { return local().getNodeType(); } @Override public String getNodeValue() { return local().getNodeValue(); } @Override public Document getOwnerDocument() { return local().getOwnerDocument(); } @Override public Element getParentElement() { return local().getParentElement(); } @Override public Node getParentNode() { return local().getParentNode(); } @Override public Node getPreviousSibling() { return local().getPreviousSibling(); } @Override public boolean hasChildNodes() { return local().hasChildNodes(); } @Override public boolean hasParentElement() { return DomNodeStatic.hasParentElement(this); } @Override public int indexInParentChildren() { return local().indexInParentChildren(); } @Override public Node insertAfter(Node newChild, Node refChild) { return DomNodeStatic.insertAfter(this, newChild, refChild); } @Override public Node insertBefore(Node newChild, Node refChild) { try { // new child first validateInsert(newChild); doPreTreeResolution(newChild); doPreTreeResolution(refChild); Node result = local().insertBefore(newChild, refChild); remote().insertBefore(newChild, refChild); return result; } catch (Exception e) { throw new LocalDomException(e); } } @Override public Node insertFirst(Node child) { return DomNodeStatic.insertFirst(this, child); } @Override public boolean isOrHasChild(Node child) { return local().isOrHasChild(child); } @Override public abstract Node nodeFor(); public List<Node> provideChildNodeList() { return new ChildNodeList(); } public boolean provideIsElement() { return getNodeType() == ELEMENT_NODE; } public boolean provideIsText() { return getNodeType() == TEXT_NODE; } @Override public Node removeAllChildren() { getChildNodes().forEach(n -> doPreTreeResolution(n)); return DomNodeStatic.removeAllChildren(this); } @Override public Node removeChild(Node oldChild) { doPreTreeResolution(oldChild); Node result = local().removeChild(oldChild); remote().removeChild(oldChild); LocalDom.detach(oldChild); return result; } @Override public void removeFromParent() { ensureRemoteCheck(); remote().removeFromParent(); local().removeFromParent(); } @Override public Node replaceChild(Node newChild, Node oldChild) { doPreTreeResolution(oldChild); doPreTreeResolution(newChild); remote().replaceChild(newChild, oldChild); Node result = local().replaceChild(newChild, oldChild); LocalDom.detach(oldChild); return result; } public DomNode sameTreeNodeFor(DomNode domNode) { if (domNode == null) { return null; } if (domNode instanceof LocalDomNode) { return local(); } else { return remote(); } } @Override public void setNodeValue(String nodeValue) { ensureRemoteCheck(); local().setNodeValue(nodeValue); remote().setNodeValue(nodeValue); } public Stream<Node> streamChildren() { return getChildNodes().stream(); } protected void doPreTreeResolution(Node child) { if (child != null) { boolean ensureBecauseChildResolved = (child.wasResolved() || child.linkedToRemote()) && (!linkedToRemote() || isPendingResolution()); if (ensureBecauseChildResolved) { LocalDom.ensureRemote(this); } boolean linkedBecauseFlushed = ensureRemoteCheck(); if (linkedToRemote() && (wasResolved() || child.wasResolved())) { if (child.wasResolved()) { LocalDom.ensureRemote(child); } else { LocalDom.ensureRemoteNodeMaybePendingResolution(child); } } } } /** * If the node was flushed, then we need to link to the remote (or our * local/remote will be inconsistent) * */ protected boolean ensureRemoteCheck() { if (!linkedToRemote() && wasResolved() && provideSelfOrAncestorLinkedToRemote() != null && !LocalDom.isDisableRemoteWrite() && (provideIsText() || provideIsElement())) { LocalDom.ensureRemote(this); return true; } else { return false; } } protected boolean isPendingResolution() { return false; } protected abstract boolean linkedToRemote(); protected abstract <T extends NodeLocal> T local(); protected Node provideRoot() { if (getParentElement() != null) { return getParentElement().provideRoot(); } return this; } protected Node provideSelfOrAncestorLinkedToRemote() { if (linkedToRemote()) { return this; } if (getParentElement() != null) { return getParentElement().provideSelfOrAncestorLinkedToRemote(); } return null; } protected abstract void putRemote(NodeRemote nodeDom, boolean resolved); protected abstract <T extends DomNode> T remote(); protected void resetRemote() { clearResolved(); resetRemote0(); } protected abstract void resetRemote0(); protected abstract NodeRemote typedRemote(); protected void validateInsert(Node newChild) { } /** * only call on reparse */ void clearResolved() { resolvedEventId = 0; } void resolved(int wasResolvedEventId) { Preconditions.checkState(this.resolvedEventId == 0 || this.resolvedEventId == wasResolvedEventId); this.resolvedEventId = wasResolvedEventId; } boolean wasResolved() { return resolvedEventId > 0; } class ChildNodeList extends AbstractList<Node> { @Override public Node get(int index) { return local().getChildren().get(index).node; } @Override public int size() { return local().getChildren().size(); } } }