001: package com.canoo.webtest.extension.applet;
002:
003: import com.gargoylesoftware.htmlunit.html.HtmlApplet;
004: import com.gargoylesoftware.htmlunit.html.HtmlElement;
005: import com.gargoylesoftware.htmlunit.html.HtmlObject;
006: import org.apache.log4j.Logger;
007:
008: import java.io.Serializable;
009: import java.net.MalformedURLException;
010: import java.net.URL;
011: import java.util.ArrayList;
012: import java.util.HashMap;
013: import java.util.Iterator;
014: import java.util.List;
015: import java.util.Locale;
016: import java.util.Map;
017: import java.util.StringTokenizer;
018:
019: /**
020: * @author Denis N. Antonioli
021: */
022: public abstract class AbstractAppletTag implements Serializable {
023: private static final Logger LOG = Logger
024: .getLogger(AbstractAppletTag.class);
025: private final URL fBase;
026: private final Map fParameter;
027: /**
028: * Specifies the initial width of the applet's display area (excluding any windows or dialogs that the applet creates).
029: * <p>Required attribute.
030: */
031: public static final String ATTR_WIDTH = "WIDTH";
032: /**
033: * Specifies the initial height of the applet's display area (excluding any windows or dialogs that the applet
034: * creates). <p>Required attribute.
035: */
036: public static final String ATTR_HEIGHT = "HEIGHT";
037: /**
038: * Specifies either the name of the class file that contains the applet's compiled applet subclass or the path to get
039: * the class, including the class file itself. It is interpreted with respect to the applet's codebase. <p>One of
040: * {@link #ATTR_CODE code} or {@link #ATTR_OBJECT object} must be present.
041: */
042: public static final String ATTR_CODE = "CODE";
043: /**
044: * Specifies a name for the applet instance, which makes it possible for applets on the same page to find (and
045: * communicate with) each other.
046: */
047: public static final String ATTR_NAME = "NAME";
048: /**
049: * Specifies the base URI for the applet. If this attribute is not specified, then it defaults the same base URI as for
050: * the current document. Values for this attribute may only refer to subdirectories of the directory containing the
051: * current document.
052: */
053: public static final String ATTR_CODEBASE = "CODEBASE";
054: /**
055: * Names a resource containing a serialized representation of an applet's state. It is interpreted relative to the
056: * applet's codebase. The serialized data contains the applet's class name but not the implementation. The class name
057: * is used to retrieve the implementation from a class file or archive. <p>When the applet is "deserialized" the
058: * start() method is invoked but not the init() method. Attributes valid when the original object was serialized are
059: * not restored. Any attributes passed to this APPLET instance will be available to the applet. Authors should use this
060: * feature with extreme caution. An applet should be stopped before it is serialized. <p>Either code or object must be
061: * present. If both {@link #ATTR_CODE code} and {@link #ATTR_OBJECT object} are given, it is an error if they provide
062: * different class names.
063: */
064: public static final String ATTR_OBJECT = "OBJECT";
065: public static final String ATTR_ARCHIVE = "ARCHIVE";
066:
067: static final List PARAM;
068:
069: static {
070: PARAM = new ArrayList();
071: PARAM.add("param");
072: }
073:
074: {
075: fParameter = new HashMap();
076: addParameterObject(AbstractAppletTag.ATTR_ARCHIVE,
077: new ArrayList());
078: addParameterLength(AbstractAppletTag.ATTR_WIDTH, "0");
079: addParameterLength(AbstractAppletTag.ATTR_HEIGHT, "0");
080: }
081:
082: public AbstractAppletTag(String base) throws MalformedURLException {
083: fBase = makeDirectoryURL(base);
084: addParameterObject(AbstractAppletTag.ATTR_CODEBASE, fBase);
085: }
086:
087: protected static URL makeDirectoryURL(String base)
088: throws MalformedURLException {
089: final URL url = new URL(base);
090: String path = url.getPath();
091: int lastSlash = path.lastIndexOf('/');
092: return new URL(url.getProtocol(), url.getHost(), url.getPort(),
093: path.substring(0, 1 + lastSlash));
094: }
095:
096: public void setArchive(String value) {
097: List archive = (List) getParameterObject(AbstractAppletTag.ATTR_ARCHIVE);
098: for (StringTokenizer st = new StringTokenizer(value, ", "); st
099: .hasMoreTokens();) {
100: archive.add(st.nextToken());
101: }
102: }
103:
104: public List getArchive() {
105: return (List) getParameterObject(AbstractAppletTag.ATTR_ARCHIVE);
106: }
107:
108: public String getCode() {
109: String code = getParameter(AbstractAppletTag.ATTR_CODE);
110: if (code.endsWith(".class")) {
111: return code.substring(0, code.length() - ".class".length());
112: }
113: return code;
114: }
115:
116: /**
117: * Specifies the base URI for the current document.
118: */
119: public URL getBase() {
120: return fBase;
121: }
122:
123: public URL getCodebase() {
124: return (URL) getParameterObject(AbstractAppletTag.ATTR_CODEBASE);
125: }
126:
127: /**
128: * Sets the code base URI. Resolves the uri in the context of the existing URI if it is relative.
129: *
130: * @param codeBase The new code base URI.
131: * @throws java.net.MalformedURLException
132: */
133: public void setCodebase(final String codeBase)
134: throws MalformedURLException {
135: URL codebase = new URL(fBase, codeBase
136: + (codeBase.endsWith("/") ? "" : "/"));
137: if (!codebase.getHost().equals(fBase.getHost())) {
138: LOG
139: .info("According to the JavaPlugin, the URL can be relative or absolute but it should be in the domain of the current document.");
140: }
141: if (!codebase.getPath().startsWith(fBase.getPath())) {
142: LOG
143: .info("According to HTML 4.0, the codebase may only refer to subdirectories of the directory containing the current document");
144: }
145: addParameterObject(AbstractAppletTag.ATTR_CODEBASE, codebase);
146: }
147:
148: public String getHeight() {
149: return getParameter(AbstractAppletTag.ATTR_HEIGHT);
150: }
151:
152: public String getName() {
153: return getParameter(AbstractAppletTag.ATTR_NAME);
154: }
155:
156: public void addParameter(final String name, final String value)
157: throws MalformedURLException {
158: if (AbstractAppletTag.ATTR_WIDTH.equalsIgnoreCase(name)
159: || AbstractAppletTag.ATTR_HEIGHT.equalsIgnoreCase(name)) {
160: addParameterLength(name, value);
161: } else if (AbstractAppletTag.ATTR_CODEBASE
162: .equalsIgnoreCase(name)) {
163: setCodebase(value);
164: } else if (AbstractAppletTag.ATTR_ARCHIVE
165: .equalsIgnoreCase(name)) {
166: setArchive(value);
167: } else {
168: addParameterObject(name, value);
169: }
170: }
171:
172: protected void addParameterObject(final String key,
173: final Object base) {
174: fParameter.put(key.toUpperCase(Locale.US), base);
175: }
176:
177: private Object getParameterObject(final String key) {
178: return fParameter.get(key.toUpperCase(Locale.US));
179: }
180:
181: /**
182: * Adds an html length to the parameter map. In html, a length is either an absolute number of pixel,
183: * <code>[<b>0</b>-<b>9</b>]+</code>, or a percentage of the available space, <code>[<b>0</b>-<b>9</b>]+<b>%</b></code>.
184: *
185: * @param name The name of the parameter.
186: * @param length The value of the parameter.
187: * @throws NumberFormatException If value is not an html length.
188: */
189: public void addParameterLength(final String name,
190: final String length) throws NumberFormatException {
191: if (length.endsWith("%")) {
192: Integer.parseInt(length.substring(0, length.length() - 1));
193: } else {
194: Integer.parseInt(length);
195: }
196: addParameterObject(name, length);
197: }
198:
199: /**
200: * Browser Note:Ê Although at least one Java-enabled browser conducts a case-sensitive search, the expected behavior is
201: * for the getApplet method to perform a case-insensitive search. For example, getApplet("old pal") and getApplet("OLD
202: * PAL") should both find an applet named "Old Pal".
203: *
204: * @param name The name of the parameter.
205: * @return The value for the parameter, or null.
206: */
207: public String getParameter(final String name) {
208: Object value = fParameter.get(name.toUpperCase(Locale.US));
209: if (value == null) {
210: return null;
211: }
212: if (value instanceof URL) {
213: return ((URL) value).toExternalForm();
214: }
215: if (value instanceof List) {
216: StringBuffer arc = new StringBuffer();
217: for (Iterator iterator = ((List) value).iterator(); iterator
218: .hasNext();) {
219: arc.append((String) iterator.next()).append(", ");
220: }
221: if (arc.length() > 2) {
222: arc.setLength(arc.length() - 2);
223: }
224: return arc.toString();
225: }
226: return (String) value;
227: }
228:
229: public String getWidth() {
230: return getParameter(AbstractAppletTag.ATTR_WIDTH);
231: }
232:
233: public URL[] getArchiveURL() throws MalformedURLException {
234: List archive = (List) getParameterObject(AbstractAppletTag.ATTR_ARCHIVE);
235: URL[] urls = new URL[archive.size() + 1];
236: URL codebase = getCodebase();
237: for (int i = 0; i < urls.length - 1; i++) {
238: urls[i] = new URL(codebase, (String) archive.get(i));
239: LOG.info(urls[i].toExternalForm());
240: }
241: urls[urls.length - 1] = codebase;
242: return urls;
243: }
244:
245: /**
246: * @param base The base URI for the current document.
247: * @param appletNode
248: * @return The parsed element.
249: * @throws NoSuchFieldException If a required attribute is missing from the html declaration.
250: * @throws java.net.MalformedURLException If there is an error in the base or the codebase attribute.
251: * @throws IllegalArgumentException If the name of the element isn't known.
252: */
253: static AbstractAppletTag newInstance(final URL base,
254: final HtmlElement appletNode) throws NoSuchFieldException,
255: MalformedURLException, IllegalArgumentException {
256: AbstractAppletTag appletTag;
257: if (appletNode instanceof HtmlApplet) {
258: appletTag = new AppletTag(base.toExternalForm());
259: } else if (appletNode instanceof HtmlObject) {
260: appletTag = new ObjectTag(base.toExternalForm());
261: } else {
262: throw new IllegalArgumentException(
263: "Don't know how to handle element <"
264: + appletNode.getTagName() + ">.");
265: }
266:
267: appletTag.addsAllAttributes(appletNode);
268: appletTag.addsAllParameters(appletNode);
269: return appletTag;
270: }
271:
272: protected abstract void addsAllAttributes(
273: final HtmlElement appletNode) throws NoSuchFieldException,
274: MalformedURLException;
275:
276: protected void addsAllParameters(HtmlElement appletNode)
277: throws NoSuchFieldException, MalformedURLException {
278: for (Iterator iterator = appletNode.getHtmlElementsByTagNames(
279: PARAM).iterator(); iterator.hasNext();) {
280: HtmlElement paramNode = (HtmlElement) iterator.next();
281: String name = paramNode.getAttributeValue("name");
282: if (name == HtmlElement.ATTRIBUTE_NOT_DEFINED) {
283: throw new NoSuchFieldException(
284: "Attribute name of param is missing.");
285: }
286: String value = paramNode.getAttributeValue("value");
287: if (value == HtmlElement.ATTRIBUTE_NOT_DEFINED) {
288: LOG.info("Skipping parameter named '" + name
289: + "', it has no value.");
290: } else {
291: addParameter(name, value);
292: }
293: }
294: }
295: }
|