001package org.jsoup.nodes; 002 003import org.jsoup.Connection; 004import org.jsoup.Jsoup; 005import org.jsoup.helper.HttpConnection; 006import org.jsoup.helper.Validate; 007import org.jsoup.parser.Tag; 008import org.jsoup.select.Elements; 009 010import java.util.ArrayList; 011import java.util.List; 012 013/** 014 * A HTML Form Element provides ready access to the form fields/controls that are associated with it. It also allows a 015 * form to easily be submitted. 016 */ 017public class FormElement extends Element { 018 private final Elements elements = new Elements(); 019 020 /** 021 * Create a new, standalone form element. 022 * 023 * @param tag tag of this element 024 * @param baseUri the base URI 025 * @param attributes initial attributes 026 */ 027 public FormElement(Tag tag, String baseUri, Attributes attributes) { 028 super(tag, baseUri, attributes); 029 } 030 031 /** 032 * Get the list of form control elements associated with this form. 033 * @return form controls associated with this element. 034 */ 035 public Elements elements() { 036 return elements; 037 } 038 039 /** 040 * Add a form control element to this form. 041 * @param element form control to add 042 * @return this form element, for chaining 043 */ 044 public FormElement addElement(Element element) { 045 elements.add(element); 046 return this; 047 } 048 049 @Override 050 protected void removeChild(Node out) { 051 super.removeChild(out); 052 elements.remove(out); 053 } 054 055 /** 056 Prepare to submit this form. A Connection object is created with the request set up from the form values. This 057 Connection will inherit the settings and the cookies (etc) of the connection/session used to request this Document 058 (if any), as available in {@link Document#connection()} 059 <p>You can then set up other options (like user-agent, timeout, cookies), then execute it.</p> 060 061 @return a connection prepared from the values of this form, in the same session as the one used to request it 062 @throws IllegalArgumentException if the form's absolute action URL cannot be determined. Make sure you pass the 063 document's base URI when parsing. 064 */ 065 public Connection submit() { 066 String action = hasAttr("action") ? absUrl("action") : baseUri(); 067 Validate.notEmpty(action, "Could not determine a form action URL for submit. Ensure you set a base URI when parsing."); 068 Connection.Method method = attr("method").equalsIgnoreCase("POST") ? 069 Connection.Method.POST : Connection.Method.GET; 070 071 Document owner = ownerDocument(); 072 Connection connection = owner != null? owner.connection().newRequest() : Jsoup.newSession(); 073 return connection.url(action) 074 .data(formData()) 075 .method(method); 076 } 077 078 /** 079 * Get the data that this form submits. The returned list is a copy of the data, and changes to the contents of the 080 * list will not be reflected in the DOM. 081 * @return a list of key vals 082 */ 083 public List<Connection.KeyVal> formData() { 084 ArrayList<Connection.KeyVal> data = new ArrayList<>(); 085 086 // iterate the form control elements and accumulate their values 087 for (Element el: elements) { 088 if (!el.tag().isFormSubmittable()) continue; // contents are form listable, superset of submitable 089 if (el.hasAttr("disabled")) continue; // skip disabled form inputs 090 String name = el.attr("name"); 091 if (name.length() == 0) continue; 092 String type = el.attr("type"); 093 094 if (type.equalsIgnoreCase("button") || type.equalsIgnoreCase("image")) continue; // browsers don't submit these 095 096 if (el.nameIs("select")) { 097 Elements options = el.select("option[selected]"); 098 boolean set = false; 099 for (Element option: options) { 100 data.add(HttpConnection.KeyVal.create(name, option.val())); 101 set = true; 102 } 103 if (!set) { 104 Element option = el.selectFirst("option"); 105 if (option != null) 106 data.add(HttpConnection.KeyVal.create(name, option.val())); 107 } 108 } else if ("checkbox".equalsIgnoreCase(type) || "radio".equalsIgnoreCase(type)) { 109 // only add checkbox or radio if they have the checked attribute 110 if (el.hasAttr("checked")) { 111 final String val = el.val().length() > 0 ? el.val() : "on"; 112 data.add(HttpConnection.KeyVal.create(name, val)); 113 } 114 } else { 115 data.add(HttpConnection.KeyVal.create(name, el.val())); 116 } 117 } 118 return data; 119 } 120 121 @Override 122 public FormElement clone() { 123 return (FormElement) super.clone(); 124 } 125}