null. The map will be stored as is, not copied.
*/
protected HtmlElement(final String namespaceURI, final String qualifiedName, final Page page,
final Mapnode.
*
* @param indent white space to indent child nodes
* @param printWriter writer where child nodes are written
*/
@Override
protected void printXml(final String indent, final PrintWriter printWriter) {
final boolean hasChildren = (getFirstChild() != null);
printWriter.print(indent + "<");
printOpeningTagContentAsXml(printWriter);
if (!hasChildren && !isEmptyXmlTagExpanded()) {
printWriter.println("/>");
}
else {
printWriter.println(">");
printChildrenAsXml(indent, printWriter);
printWriter.println(indent + "" + getTagName() + ">");
}
}
/**
* Indicates if a node without children should be written in expanded form as XML
* (i.e. with closing tag rather than with "/>")
* @return false by default
*/
protected boolean isEmptyXmlTagExpanded() {
return false;
}
/**
* Prints the content between "<" and ">" (or "/>") in the output of the tag name
* and its attributes in XML format.
* @param printWriter the writer to print in
*/
protected void printOpeningTagContentAsXml(final PrintWriter printWriter) {
printWriter.print(getTagName());
for (final String name : attributes_.keySet()) {
printWriter.print(" ");
printWriter.print(name);
printWriter.print("=\"");
printWriter.print(StringEscapeUtils.escapeXml(attributes_.get(name).getNodeValue()));
printWriter.print("\"");
}
}
/**
* Returns a string representation of this object.
* @return a string representation of this object
*/
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
buffer.append(ClassUtils.getShortClassName(getClass()));
buffer.append("[<");
final StringWriter writer = new StringWriter();
final PrintWriter printWriter = new PrintWriter(writer);
printOpeningTagContentAsXml(printWriter);
buffer.append(writer.toString());
buffer.append(">]");
return buffer.toString();
}
/**
* Searches for an element based on the specified criteria, returning the first element which matches
* said criteria. Only elements which are descendants of this element are included in the search.
*
* @param elementName the name of the element to search for
* @param attributeName the name of the attribute to search for
* @param attributeValue the value of the attribute to search for
* @return the first element which matches the specified search criteria
* @exception ElementNotFoundException if no element matches the specified search criteria
*/
public final HtmlElement getOneHtmlElementByAttribute(final String elementName, final String attributeName,
final String attributeValue) throws ElementNotFoundException {
WebAssert.notNull("elementName", elementName);
WebAssert.notNull("attributeName", attributeName);
WebAssert.notNull("attributeValue", attributeValue);
final List< ? extends HtmlElement> list =
getHtmlElementsByAttribute(elementName, attributeName, attributeValue);
final int listSize = list.size();
if (listSize == 0) {
throw new ElementNotFoundException(elementName, attributeName, attributeValue);
}
return list.get(0);
}
/**
* Returns the element in this element's page with the specified ID. If more than one element
* has the specified ID (not allowed by the HTML spec), this method returns the first one.
*
* @param id the ID value to search for
* @return the element in this element's page with the specified ID
* @exception ElementNotFoundException if no element has the specified ID
*/
public HtmlElement getHtmlElementById(final String id) throws ElementNotFoundException {
return ((HtmlPage) getPage()).getHtmlElementById(id);
}
/**
* Returns true if there is an element in this element's page with the specified ID. * This method is intended for situations where it is enough to know whether a specific * element is present in the document.
* *Implementation Note: This method calls {@link #getHtmlElementById(String)} internally, * so writing code such as the following would be extremely inefficient:
* *
* if (hasHtmlElementWithId(id)) {
* HtmlElement element = getHtmlElementWithId(id)
* ...
* }
*
*
* @param id the id to search for
* @return true if there is an element in this element's page with the specified ID
*/
public boolean hasHtmlElementWithId(final String id) {
try {
getHtmlElementById(id);
return true;
}
catch (final ElementNotFoundException e) {
return false;
}
}
/**
* Returns all elements which are descendants of this element and match the specified search criteria.
*
* @param elementName the name of the element to search for
* @param attributeName the name of the attribute to search for
* @param attributeValue the value of the attribute to search for
* @return all elements which are descendants of this element and match the specified search criteria
*/
public final List< ? extends HtmlElement> getHtmlElementsByAttribute(
final String elementName,
final String attributeName,
final String attributeValue) {
final Listnull if nothing is executed
*/
public ScriptResult fireEvent(final String eventType) {
return fireEvent(new Event(this, eventType));
}
/**
* Fires the event on the element. Nothing is done if JavaScript is disabled.
* @param event the event to fire
* @return the execution result, or null if nothing is executed
*/
public ScriptResult fireEvent(final Event event) {
if (!getPage().getWebClient().isJavaScriptEnabled()) {
return null;
}
if (mainLog_.isDebugEnabled()) {
mainLog_.debug("Firing " + event);
}
final HTMLElement jsElt = (HTMLElement) getScriptObject();
final ContextAction action = new ContextAction() {
public Object run(final Context cx) {
return jsElt.fireEvent(event);
}
};
final ScriptResult result = (ScriptResult) ContextFactory.getGlobal().call(action);
final boolean isIE = getPage().getWebClient().getBrowserVersion().isIE();
if ((!isIE && event.isPreventDefault()) || (isIE && ScriptResult.isFalse(result))) {
preventDefault();
}
return result;
}
/**
* This method is called if the current fired event is canceled by preventDefault() in FireFox,
* or by returning false in Internet Explorer.
*
* The default implementation does nothing.
*/
protected void preventDefault() {
// Empty by default; override as needed.
}
/**
* Simulates moving the mouse over this element, returning the page which this element's window contains
* after the mouse move. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @return the page which this element's window contains after the mouse move
*/
public Page mouseOver() {
return mouseOver(false, false, false, MouseEvent.BUTTON_LEFT);
}
/**
* Simulates moving the mouse over this element, returning the page which this element's window contains
* after the mouse move. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @param shiftKey true if SHIFT is pressed during the mouse move
* @param ctrlKey true if CTRL is pressed during the mouse move
* @param altKey true if ALT is pressed during the mouse move
* @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
* or {@link MouseEvent#BUTTON_RIGHT}
* @return the page which this element's window contains after the mouse move
*/
public Page mouseOver(final boolean shiftKey, final boolean ctrlKey, final boolean altKey, final int button) {
return doMouseEvent(MouseEvent.TYPE_MOUSE_OVER, shiftKey, ctrlKey, altKey, button);
}
/**
* Simulates moving the mouse over this element, returning the page which this element's window contains
* after the mouse move. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @return the page which this element's window contains after the mouse move
*/
public Page mouseMove() {
return mouseMove(false, false, false, MouseEvent.BUTTON_LEFT);
}
/**
* Simulates moving the mouse over this element, returning the page which this element's window contains
* after the mouse move. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @param shiftKey true if SHIFT is pressed during the mouse move
* @param ctrlKey true if CTRL is pressed during the mouse move
* @param altKey true if ALT is pressed during the mouse move
* @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
* or {@link MouseEvent#BUTTON_RIGHT}
* @return the page which this element's window contains after the mouse move
*/
public Page mouseMove(final boolean shiftKey, final boolean ctrlKey, final boolean altKey, final int button) {
return doMouseEvent(MouseEvent.TYPE_MOUSE_MOVE, shiftKey, ctrlKey, altKey, button);
}
/**
* Simulates moving the mouse out of this element, returning the page which this element's window contains
* after the mouse move. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @return the page which this element's window contains after the mouse move
*/
public Page mouseOut() {
return mouseOut(false, false, false, MouseEvent.BUTTON_LEFT);
}
/**
* Simulates moving the mouse out of this element, returning the page which this element's window contains
* after the mouse move. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @param shiftKey true if SHIFT is pressed during the mouse move
* @param ctrlKey true if CTRL is pressed during the mouse move
* @param altKey true if ALT is pressed during the mouse move
* @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
* or {@link MouseEvent#BUTTON_RIGHT}
* @return the page which this element's window contains after the mouse move
*/
public Page mouseOut(final boolean shiftKey, final boolean ctrlKey, final boolean altKey, final int button) {
return doMouseEvent(MouseEvent.TYPE_MOUSE_OUT, shiftKey, ctrlKey, altKey, button);
}
/**
* Simulates clicking the mouse on this element, returning the page which this element's window contains
* after the mouse click. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @return the page which this element's window contains after the mouse click
*/
public Page mouseDown() {
return mouseDown(false, false, false, MouseEvent.BUTTON_LEFT);
}
/**
* Simulates clicking the mouse on this element, returning the page which this element's window contains
* after the mouse click. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @param shiftKey true if SHIFT is pressed during the mouse click
* @param ctrlKey true if CTRL is pressed during the mouse click
* @param altKey true if ALT is pressed during the mouse click
* @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
* or {@link MouseEvent#BUTTON_RIGHT}
* @return the page which this element's window contains after the mouse click
*/
public Page mouseDown(final boolean shiftKey, final boolean ctrlKey, final boolean altKey, final int button) {
return doMouseEvent(MouseEvent.TYPE_MOUSE_DOWN, shiftKey, ctrlKey, altKey, button);
}
/**
* Simulates releasing the mouse click on this element, returning the page which this element's window contains
* after the mouse click release. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @return the page which this element's window contains after the mouse click release
*/
public Page mouseUp() {
return mouseUp(false, false, false, MouseEvent.BUTTON_LEFT);
}
/**
* Simulates releasing the mouse click on this element, returning the page which this element's window contains
* after the mouse click release. The returned page may or may not be the same as the original page, depending
* on JavaScript event handlers, etc.
*
* @param shiftKey true if SHIFT is pressed during the mouse click release
* @param ctrlKey true if CTRL is pressed during the mouse click release
* @param altKey true if ALT is pressed during the mouse click release
* @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
* or {@link MouseEvent#BUTTON_RIGHT}
* @return the page which this element's window contains after the mouse click release
*/
public Page mouseUp(final boolean shiftKey, final boolean ctrlKey, final boolean altKey, final int button) {
return doMouseEvent(MouseEvent.TYPE_MOUSE_UP, shiftKey, ctrlKey, altKey, button);
}
/**
* Simulates right clicking the mouse on this element, returning the page which this element's window
* contains after the mouse click. The returned page may or may not be the same as the original page,
* depending on JavaScript event handlers, etc.
*
* @return the page which this element's window contains after the mouse click
*/
public Page rightClick() {
return rightClick(false, false, false);
}
/**
* Simulates right clicking the mouse on this element, returning the page which this element's window
* contains after the mouse click. The returned page may or may not be the same as the original page,
* depending on JavaScript event handlers, etc.
*
* @param shiftKey true if SHIFT is pressed during the mouse click
* @param ctrlKey true if CTRL is pressed during the mouse click
* @param altKey true if ALT is pressed during the mouse click
* @return the page which this element's window contains after the mouse click
*/
public Page rightClick(final boolean shiftKey, final boolean ctrlKey, final boolean altKey) {
final Page mouseDownPage = mouseDown(shiftKey, ctrlKey, altKey, MouseEvent.BUTTON_RIGHT);
if (mouseDownPage != getPage()) {
if (mainLog_.isDebugEnabled()) {
mainLog_.debug("rightClick() is incomplete, as mouseDown() loaded a different page.");
}
return mouseDownPage;
}
final Page mouseUpPage = mouseUp(shiftKey, ctrlKey, altKey, MouseEvent.BUTTON_RIGHT);
if (mouseUpPage != getPage()) {
if (mainLog_.isDebugEnabled()) {
mainLog_.debug("rightClick() is incomplete, as mouseUp() loaded a different page.");
}
return mouseUpPage;
}
return doMouseEvent(MouseEvent.TYPE_CONTEXT_MENU, shiftKey, ctrlKey, altKey, MouseEvent.BUTTON_RIGHT);
}
/**
* Simulates the specified mouse event, returning the page which this element's window contains after the event.
* The returned page may or may not be the same as the original page, depending on JavaScript event handlers, etc.
*
* @param eventType the mouse event type to simulate
* @param shiftKey true if SHIFT is pressed during the mouse event
* @param ctrlKey true if CTRL is pressed during the mouse event
* @param altKey true if ALT is pressed during the mouse event
* @param button the button code, must be {@link MouseEvent#BUTTON_LEFT}, {@link MouseEvent#BUTTON_MIDDLE}
* or {@link MouseEvent#BUTTON_RIGHT}
* @return the page which this element's window contains after the event
*/
private Page doMouseEvent(final String eventType, final boolean shiftKey, final boolean ctrlKey,
final boolean altKey, final int button) {
if (this instanceof DisabledElement && ((DisabledElement) this).isDisabled()) {
return getPage();
}
final HtmlPage page = (HtmlPage) getPage();
final Event event = new MouseEvent(this, eventType, shiftKey, ctrlKey, altKey, button);
final ScriptResult scriptResult = fireEvent(event);
final Page currentPage;
if (scriptResult == null) {
currentPage = page;
}
else {
currentPage = scriptResult.getNewPage();
}
return currentPage;
}
/**
* Removes focus from this element.
*/
public void blur() {
((HtmlPage) getPage()).setFocusedElement(null);
}
/**
* Sets the focus on this element.
*/
public void focus() {
((HtmlPage) getPage()).setFocusedElement(this);
final WebClient webClient = getPage().getWebClient();
if (webClient.getBrowserVersion().isIE()) {
final HTMLElement jsElt = (HTMLElement) getScriptObject();
jsElt.jsxFunction_setActive();
}
}
/**
* {@inheritDoc}
*/
@Override
public SgmlPage getPage() {
return (SgmlPage) super.getPage();
}
/**
* {@inheritDoc}
*/
@Override
protected void checkChildHierarchy(final Node childNode) throws DOMException {
if (!((childNode instanceof Element) || (childNode instanceof Text)
|| (childNode instanceof Comment) || (childNode instanceof ProcessingInstruction)
|| (childNode instanceof CDATASection) || (childNode instanceof EntityReference))) {
throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
"The Element may not have a child of this type: " + childNode.getNodeType());
}
super.checkChildHierarchy(childNode);
}
/**
* Custom serialization logic which ensures that {@link NonSerializable} {@link HtmlAttributeChangeListener}s
* are not serialized.
* @param stream the stream to write the object to
* @throws IOException if an error occurs during writing
*/
private void writeObject(final ObjectOutputStream stream) throws IOException {
synchronized (this) {
// Store the original list of listeners in a temporary variable, and then
// modify the listener list by removing any NonSerializable listeners.
final List