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 /** 050 * Prepare to submit this form. A Connection object is created with the request set up from the form values. You 051 * can then set up other options (like user-agent, timeout, cookies), then execute it. 052 * @return a connection prepared from the values of this form. 053 * @throws IllegalArgumentException if the form's absolute action URL cannot be determined. Make sure you pass the 054 * document's base URI when parsing. 055 */ 056 public Connection submit() { 057 String action = hasAttr("action") ? absUrl("action") : baseUri(); 058 Validate.notEmpty(action, "Could not determine a form action URL for submit. Ensure you set a base URI when parsing."); 059 Connection.Method method = attr("method").toUpperCase().equals("POST") ? 060 Connection.Method.POST : Connection.Method.GET; 061 062 return Jsoup.connect(action) 063 .data(formData()) 064 .method(method); 065 } 066 067 /** 068 * Get the data that this form submits. The returned list is a copy of the data, and changes to the contents of the 069 * list will not be reflected in the DOM. 070 * @return a list of key vals 071 */ 072 public List<Connection.KeyVal> formData() { 073 ArrayList<Connection.KeyVal> data = new ArrayList<>(); 074 075 // iterate the form control elements and accumulate their values 076 for (Element el: elements) { 077 if (!el.tag().isFormSubmittable()) continue; // contents are form listable, superset of submitable 078 if (el.hasAttr("disabled")) continue; // skip disabled form inputs 079 String name = el.attr("name"); 080 if (name.length() == 0) continue; 081 String type = el.attr("type"); 082 083 if ("select".equals(el.tagName())) { 084 Elements options = el.select("option[selected]"); 085 boolean set = false; 086 for (Element option: options) { 087 data.add(HttpConnection.KeyVal.create(name, option.val())); 088 set = true; 089 } 090 if (!set) { 091 Element option = el.select("option").first(); 092 if (option != null) 093 data.add(HttpConnection.KeyVal.create(name, option.val())); 094 } 095 } else if ("checkbox".equalsIgnoreCase(type) || "radio".equalsIgnoreCase(type)) { 096 // only add checkbox or radio if they have the checked attribute 097 if (el.hasAttr("checked")) { 098 final String val = el.val().length() > 0 ? el.val() : "on"; 099 data.add(HttpConnection.KeyVal.create(name, val)); 100 } 101 } else { 102 data.add(HttpConnection.KeyVal.create(name, el.val())); 103 } 104 } 105 return data; 106 } 107}