mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-24 23:32:05 +01:00
Merge pull request #459 from Flowdalic/reported-data
[DataFormProvider] Retrieve field types from <reported/> if possible
This commit is contained in:
commit
ce4f85acf8
4 changed files with 173 additions and 11 deletions
|
@ -360,8 +360,7 @@ public final class FileTransferNegotiator extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataForm createDefaultInitiationForm() {
|
private static DataForm createDefaultInitiationForm() {
|
||||||
DataForm.Builder form = DataForm.builder()
|
DataForm.Builder form = DataForm.builder(DataForm.Type.form);
|
||||||
.setType(DataForm.Type.form);
|
|
||||||
ListSingleFormField.Builder fieldBuilder = FormField.listSingleBuilder(STREAM_DATA_FIELD_NAME);
|
ListSingleFormField.Builder fieldBuilder = FormField.listSingleBuilder(STREAM_DATA_FIELD_NAME);
|
||||||
|
|
||||||
if (!IBB_ONLY) {
|
if (!IBB_ONLY) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software, 2020 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software, 2020-2021 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -353,6 +353,7 @@ public final class DataForm implements ExtensionElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
|
// TODO: Make this field final once setType() is gone.
|
||||||
private Type type;
|
private Type type;
|
||||||
private String title;
|
private String title;
|
||||||
private List<String> instructions;
|
private List<String> instructions;
|
||||||
|
@ -381,6 +382,15 @@ public final class DataForm implements ExtensionElement {
|
||||||
extensionElements = CollectionUtil.newListWith(dataForm.getExtensionElements());
|
extensionElements = CollectionUtil.newListWith(dataForm.getExtensionElements());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated do not use.
|
||||||
|
*
|
||||||
|
* @param type the type.
|
||||||
|
* @return a reference to this builder.
|
||||||
|
* @deprecated use {@link DataForm#builder(Type)} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
// TODO: Remove in Smack 4.5 and then make this.type final.
|
||||||
public Builder setType(Type type) {
|
public Builder setType(Type type) {
|
||||||
this.type = Objects.requireNonNull(type);
|
this.type = Objects.requireNonNull(type);
|
||||||
return this;
|
return this;
|
||||||
|
@ -538,6 +548,8 @@ public final class DataForm implements ExtensionElement {
|
||||||
|
|
||||||
private final List<? extends FormField> fields;
|
private final List<? extends FormField> fields;
|
||||||
|
|
||||||
|
private Map<String, FormField> fieldMap;
|
||||||
|
|
||||||
public ReportedData(List<? extends FormField> fields) {
|
public ReportedData(List<? extends FormField> fields) {
|
||||||
this.fields = Collections.unmodifiableList(fields);
|
this.fields = Collections.unmodifiableList(fields);
|
||||||
}
|
}
|
||||||
|
@ -561,6 +573,18 @@ public final class DataForm implements ExtensionElement {
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FormField getField(String name) {
|
||||||
|
if (fieldMap == null) {
|
||||||
|
fieldMap = new HashMap<>(fields.size());
|
||||||
|
for (FormField field : fields) {
|
||||||
|
String fieldName = field.getFieldName();
|
||||||
|
fieldMap.put(fieldName, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fieldMap.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
|
||||||
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2003-2007 Jive Software 2020 Florian Schmaus.
|
* Copyright 2003-2007 Jive Software 2020-2021 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -66,10 +66,10 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
@Override
|
@Override
|
||||||
public DataForm parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
public DataForm parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
DataForm.Type dataFormType = DataForm.Type.fromString(parser.getAttributeValue("", "type"));
|
DataForm.Type dataFormType = DataForm.Type.fromString(parser.getAttributeValue("", "type"));
|
||||||
DataForm.Builder dataForm = DataForm.builder();
|
DataForm.Builder dataForm = DataForm.builder(dataFormType);
|
||||||
dataForm.setType(dataFormType);
|
|
||||||
|
|
||||||
String formType = null;
|
String formType = null;
|
||||||
|
DataForm.ReportedData reportedData = null;
|
||||||
|
|
||||||
outerloop: while (true) {
|
outerloop: while (true) {
|
||||||
XmlPullParser.Event eventType = parser.next();
|
XmlPullParser.Event eventType = parser.next();
|
||||||
|
@ -86,6 +86,8 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
dataForm.setTitle(parser.nextText());
|
dataForm.setTitle(parser.nextText());
|
||||||
break;
|
break;
|
||||||
case "field":
|
case "field":
|
||||||
|
// Note that we parse this form field without any potential reportedData. We only use reportedData
|
||||||
|
// to lookup form field types of fields under <item/>.
|
||||||
FormField formField = parseField(parser, elementXmlEnvironment, formType);
|
FormField formField = parseField(parser, elementXmlEnvironment, formType);
|
||||||
|
|
||||||
TextSingleFormField hiddenFormTypeField = formField.asHiddenFormTypeFieldIfPossible();
|
TextSingleFormField hiddenFormTypeField = formField.asHiddenFormTypeFieldIfPossible();
|
||||||
|
@ -99,12 +101,15 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
dataForm.addField(formField);
|
dataForm.addField(formField);
|
||||||
break;
|
break;
|
||||||
case "item":
|
case "item":
|
||||||
DataForm.Item item = parseItem(parser, elementXmlEnvironment, formType);
|
DataForm.Item item = parseItem(parser, elementXmlEnvironment, formType, reportedData);
|
||||||
dataForm.addItem(item);
|
dataForm.addItem(item);
|
||||||
break;
|
break;
|
||||||
case "reported":
|
case "reported":
|
||||||
DataForm.ReportedData reported = parseReported(parser, elementXmlEnvironment, formType);
|
if (reportedData != null) {
|
||||||
dataForm.setReportedData(reported);
|
throw new SmackParsingException("Data form with multiple <reported/> elements");
|
||||||
|
}
|
||||||
|
reportedData = parseReported(parser, elementXmlEnvironment, formType);
|
||||||
|
dataForm.setReportedData(reportedData);
|
||||||
break;
|
break;
|
||||||
// See XEP-133 Example 32 for a corner case where the data form contains this extension.
|
// See XEP-133 Example 32 for a corner case where the data form contains this extension.
|
||||||
case RosterPacket.ELEMENT:
|
case RosterPacket.ELEMENT:
|
||||||
|
@ -135,6 +140,11 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
|
|
||||||
private static FormField parseField(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType)
|
private static FormField parseField(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
return parseField(parser, xmlEnvironment, formType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FormField parseField(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType, DataForm.ReportedData reportedData)
|
||||||
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
final int initialDepth = parser.getDepth();
|
final int initialDepth = parser.getDepth();
|
||||||
|
|
||||||
final String fieldName = parser.getAttributeValue("var");
|
final String fieldName = parser.getAttributeValue("var");
|
||||||
|
@ -186,6 +196,16 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data forms of type 'result' may contain <reported/> and <item/> elements. If this is the case, then the type
|
||||||
|
// of the <field/>s within the <item/> elements is determined by the information found in <reported/>. See
|
||||||
|
// XEP-0004 § 3.4 and SMACK-902
|
||||||
|
if (type == null && reportedData != null) {
|
||||||
|
FormField reportedFormField = reportedData.getField(fieldName);
|
||||||
|
if (reportedFormField != null) {
|
||||||
|
type = reportedFormField.getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
// If no field type was explicitly provided, then we need to lookup the
|
// If no field type was explicitly provided, then we need to lookup the
|
||||||
// field's type in the registry.
|
// field's type in the registry.
|
||||||
|
@ -301,7 +321,8 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataForm.Item parseItem(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType)
|
private static DataForm.Item parseItem(XmlPullParser parser, XmlEnvironment xmlEnvironment, String formType,
|
||||||
|
DataForm.ReportedData reportedData)
|
||||||
throws XmlPullParserException, IOException, SmackParsingException {
|
throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
final int initialDepth = parser.getDepth();
|
final int initialDepth = parser.getDepth();
|
||||||
List<FormField> fields = new ArrayList<>();
|
List<FormField> fields = new ArrayList<>();
|
||||||
|
@ -312,7 +333,8 @@ public class DataFormProvider extends ExtensionElementProvider<DataForm> {
|
||||||
String name = parser.getName();
|
String name = parser.getName();
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "field":
|
case "field":
|
||||||
FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType);
|
FormField field = parseField(parser, XmlEnvironment.from(parser, xmlEnvironment), formType,
|
||||||
|
reportedData);
|
||||||
fields.add(field);
|
fields.add(field);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2021 Florian Schmaus
|
||||||
|
*
|
||||||
|
* 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 org.jivesoftware.smackx.xdata.provider;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.parsing.SmackParsingException;
|
||||||
|
import org.jivesoftware.smack.util.PacketParserUtils;
|
||||||
|
import org.jivesoftware.smack.xml.XmlPullParser;
|
||||||
|
import org.jivesoftware.smack.xml.XmlPullParserException;
|
||||||
|
import org.jivesoftware.smackx.xdata.FormField;
|
||||||
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class DataFormProviderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRetrieveFieldTypeFromReported() throws XmlPullParserException, IOException, SmackParsingException {
|
||||||
|
|
||||||
|
String firstForm =
|
||||||
|
"<x xmlns='jabber:x:data' type='form'>" +
|
||||||
|
" <title>Advanced User Search</title>" +
|
||||||
|
" <instructions>The following fields are available for searching. Wildcard (*) characters are allowed as part of the query.</instructions>" +
|
||||||
|
" <field var='FORM_TYPE' type='hidden'>" +
|
||||||
|
" <value>jabber:iq:search</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Search' var='search'>" +
|
||||||
|
" <required/>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Username' var='Username' type='boolean'>" +
|
||||||
|
" <value>true</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Name' var='Name' type='boolean'>" +
|
||||||
|
" <value>true</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field label='Email' var='Email' type='boolean'>" +
|
||||||
|
" <value>true</value>" +
|
||||||
|
" </field>" +
|
||||||
|
"</x>";
|
||||||
|
XmlPullParser parser = PacketParserUtils.getParserFor(firstForm);
|
||||||
|
DataForm firstDataForm = DataFormProvider.INSTANCE.parse(parser);
|
||||||
|
FormField usernameFormField = firstDataForm.getField("Username");
|
||||||
|
assertEquals(FormField.Type.bool, usernameFormField.getType());
|
||||||
|
|
||||||
|
String secondForm =
|
||||||
|
"<x xmlns='jabber:x:data' type='result'>" +
|
||||||
|
" <field var='FORM_TYPE' type='hidden'/>" +
|
||||||
|
" <reported>" +
|
||||||
|
" <field var='jid' type='jid-single' label='JID'/>" +
|
||||||
|
" <field var='Username' type='text-single' label='Username'/>" +
|
||||||
|
" <field var='Name' type='text-single' label='Name'/>" +
|
||||||
|
" <field var='Email' type='text-single' label='Email'/>" +
|
||||||
|
" </reported>" +
|
||||||
|
" <item>" +
|
||||||
|
" <field var='Email'>" +
|
||||||
|
" <value>" +
|
||||||
|
" 0" +
|
||||||
|
" </value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field var='jid'>" +
|
||||||
|
" <value>frank@orphu</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field var='Username'>" +
|
||||||
|
" <value>" +
|
||||||
|
" frank" +
|
||||||
|
" </value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field var='Name'>" +
|
||||||
|
" <value>" +
|
||||||
|
" 0" +
|
||||||
|
" </value>" +
|
||||||
|
" </field>" +
|
||||||
|
" </item>" +
|
||||||
|
" <item>" +
|
||||||
|
" <field var='Email'>" +
|
||||||
|
" <value>" +
|
||||||
|
" </value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field var='jid'>" +
|
||||||
|
" <value>frank2@orphu</value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field var='Username'>" +
|
||||||
|
" <value>" +
|
||||||
|
" frank2" +
|
||||||
|
" </value>" +
|
||||||
|
" </field>" +
|
||||||
|
" <field var='Name'>" +
|
||||||
|
" <value>" +
|
||||||
|
" </value>" +
|
||||||
|
" </field>" +
|
||||||
|
" </item>" +
|
||||||
|
"</x>";
|
||||||
|
parser = PacketParserUtils.getParserFor(secondForm);
|
||||||
|
DataForm secondDataForm = DataFormProvider.INSTANCE.parse(parser);
|
||||||
|
List<DataForm.Item> items = secondDataForm.getItems();
|
||||||
|
assertEquals(2, items.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue