Java tutorial
/* * Copyright 2011 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.user.client.ui; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.dom.builder.shared.HtmlBuilderFactory; import com.google.gwt.dom.builder.shared.HtmlElementBuilder; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.DOM; import cc.alcina.framework.gwt.client.util.ClientUtils; /** * EXPERIMENTAL and subject to change. Do not use this in production code. * <p> * A simple {@link Element} implementation (<strong>not</strong> an actual dom * object) that can serve as stand in to be used by {@link IsRenderable} widgets * before they are fully built. For example, it can accumulate simple set*() * values to be used when the widget is actually ready to render. Thus, most * {@link IsRenderable} widget code can be written without taking into account * whether or not the widget has yet been rendered. * <p> * {@link DOM#appendChild} is aware of PotentialElement, and calls its resolve() * method. This triggers a call to {@link UIObject#resolvePotentialElement()}, * which widgets can customize to get a real {@link Element} in place at the * last moment. * * TODO(rdcastro): Cover all unsupported methods with helpful error messages. */ public class PotentialElement extends Element { static { declareShim(); } public static PotentialElement as(Element e) { throw new UnsupportedOperationException(); // assert isPotential(e); // return (PotentialElement) e; } /** * Builds a new PotentialElement with the tag name set to "div". * * @see #build(UIObject,String) */ public static PotentialElement build(UIObject o) { return build(o, "div"); } public static PotentialElement build(UIObject o, String tagName) { throw new UnsupportedOperationException(); } /** * Builds a new PotentialElement. This element keeps track of the * {@link UIObject} so that it can call * {@link UIObject#resolvePotentialElement} to get a real element when that * is needed. */ // public static native PotentialElement build(UIObject o, String tagName) // /*-{ // var el = new $wnd.GwtPotentialElementShim(); // el.tagName = tagName; // el.__gwt_resolve = // @com.google.gwt.user.client.ui.PotentialElement::buildResolveCallback(Lcom/google/gwt/user/client/ui/UIObject;)(o); // return // @com.google.gwt.dom.client.Element::as(Lcom/google/gwt/core/client/JavaScriptObject;)(el); // }-*/; /** * Creates an {@link HtmlElementBuilder} instance inheriting all attributes * set for the given PotentialElement. * * @param potentialElement * assumed to be a PotentialElement, used as basis for the * builder * @return a propertly configured {@link HtmlElementBuilder} instance */ public static HtmlElementBuilder createBuilderFor(Element potentialElement) { PotentialElement el = PotentialElement.as(potentialElement); HtmlElementBuilder builder = HtmlBuilderFactory.get().trustedCreate(el.getTagName()); el.mergeInto(builder); return builder; } public static boolean isPotential(com.google.gwt.dom.client.Element elem) { return false; } /** * If given a PotentialElement, returns the real Element to be built from * it. Otherwise returns the given Element itself. * <p> * Note that a PotentialElement can only be resolved once. Making repeated * calls to this method with the same PotentialElement is an error. */ public static Element resolve(Element maybePotential) { throw new UnsupportedOperationException(); // return maybePotential.<PotentialElement> cast().resolve(); } private static native JavaScriptObject buildResolveCallback(UIObject resolver) /*-{ return function() { this.__gwt_resolve = @com.google.gwt.user.client.ui.PotentialElement::cannotResolveTwice(); return resolver.@com.google.gwt.user.client.ui.UIObject::resolvePotentialElement()(); }; }-*/; private static final native void cannotResolveTwice() /*-{ throw "A PotentialElement cannot be resolved twice."; }-*/; private static final native void declareShim() /*-{ var shim = function() { }; shim.prototype = { className : '', clientHeight : 0, clientWidth : 0, dir : '', getAttribute : function(name, value) { return this[name]; }, href : '', id : '', innerHTML : '', lang : '', // should be @com.google.gwt.dom.client.Node.ELEMENT_MODE, but the compiler // doesn't like that. nodeType : 1, removeAttribute : function(name, value) { this[name] = undefined; }, setAttribute : function(name, value) { this[name] = value; }, src : '', style : {}, title : '' }; $wnd.GwtPotentialElementShim = shim; }-*/; /** * Tests whether a given {@link JavaScriptObject} represents a * PotentialElement. * * @param o * the {@link JavaScriptObject} to be tested * @return true if the given object is a PotentialElement instance */ private static native boolean isPotential0(JavaScriptObject o) /*-{ try { return (!!o) && (!!o.__gwt_resolve); } catch (e) { return false; } }-*/; protected PotentialElement() { } public final String getInnerText0() { return ClientUtils.simpleInnerText(getInnerHTML()); } /** * Copy only the fields that have actually changed from the values in the * shim prototype. Do this by severing the __proto__ link, allowing us to * iterate only on the fields set in this specific instance. */ private native void mergeInto(HtmlElementBuilder builder) /*-{ var savedProto = this.__proto__; var tagName = this.tagName; var gwtResolve = this.__gwt_resolve; var className = this.className; var innerHTML = this.innerHTML; this.innerHTML=null; try { this.__proto__ = null; this.tagName = null; this.__gwt_resolve = null; // className needs special treatment because the actual HTML attribute is // called "class" and not "className". if (this.className) { builder.@com.google.gwt.dom.builder.shared.ElementBuilder::className(Ljava/lang/String;)( this.className); this.className = null; } // Iterate over all attributes, and copy them to the ElementBuilder. // TODO(rdcastro): Deal with the "style" attribute. for (attr in this) { if (!this[attr]) { continue; } if (typeof this[attr] == 'number') { builder.@com.google.gwt.dom.builder.shared.ElementBuilder::attribute(Ljava/lang/String;I)( attr, this[attr]); } else if (typeof this[attr] == 'string') { builder.@com.google.gwt.dom.builder.shared.ElementBuilder::attribute(Ljava/lang/String;Ljava/lang/String;)( attr, this[attr]); } } } finally { this.__proto__ = savedProto; if (className) { this.className = className; } this.__gwt_resolve = gwtResolve; this.tagName = tagName; } }-*/; /** * Calls the <code>__gwt_resolve</code> method on the underlying JavaScript * object if it exists. On objects created via {@link #build}, this method * is a call to the {@link UIObject#resolvePotentialElement} method on the * associated UIObject. */ private native Element resolve() /*-{ return this.__gwt_resolve ? this.__gwt_resolve() : this; }-*/; final native Element setResolver(UIObject resolver) /*-{ this.__gwt_resolve = @com.google.gwt.user.client.ui.PotentialElement::buildResolveCallback(Lcom/google/gwt/user/client/ui/UIObject;)(resolver); }-*/; }