diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md
index 9e29b5bae..005eddb60 100644
--- a/documentation/extensions/index.md
+++ b/documentation/extensions/index.md
@@ -48,6 +48,7 @@ Smack Extensions and currently supported XEPs by Smack (smack-extensions)
| Stream Initation | [XEP-0095](http://xmpp.org/extensions/xep-0095.html) | Initiating a data stream between any two XMPP entities. |
| [SI File Transfer](filetransfer.html) | [XEP-0096](http://xmpp.org/extensions/xep-0096.html) | Transfer files between two users over XMPP. |
| [Entity Capabilities](caps.html) | [XEP-0115](http://xmpp.org/extensions/xep-0115.html) | Broadcasting and dynamic discovery of entity capabilities. |
+| Data Forms Validation | [XEP-0122](http://xmpp.org/extensions/xep-0122.html) | Enables an application to specify additional validation guidelines . |
| Stream Compression | [XEP-0138](http://xmpp.org/extensions/xep-0138.html) | Support for optional compression of the XMPP stream.
| Data Forms Layout | [XEP-0141](http://xmpp.org/extensions/xep-0141.html) | Enables an application to specify form layouts. |
| Personal Eventing Protocol | [XEP-0163](http://xmpp.org/extensions/xep-0163.html) | Using the XMPP publish-subscribe protocol to broadcast state change events associated with a XMPP account. |
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java
index 708beb1ca..55c07e07a 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/XmlStringBuilder.java
@@ -206,14 +206,14 @@ public class XmlStringBuilder implements Appendable, CharSequence {
}
/**
- * Add the given attribute if value => 0
+ * Add the given attribute if value not null and value => 0.
*
* @param name
* @param value
* @return a reference to this object
*/
public XmlStringBuilder optLongAttribute(String name, Long value) {
- if (value >= 0) {
+ if (value != null && value >= 0) {
attribute(name, Long.toString(value));
}
return this;
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java
index 6ba006cde..924a8a573 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/FormField.java
@@ -17,12 +17,13 @@
package org.jivesoftware.smackx.xdata;
-import org.jivesoftware.smack.util.XmlStringBuilder;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;
+
/**
* Represents a field of a form. The field could be used to represent a question to complete,
* a completed question or a data returned from a search. The exact interpretation of the field
@@ -124,6 +125,7 @@ public class FormField {
private Type type;
private final List options = new ArrayList ();
private final List values = new ArrayList();
+ private ValidateElement validateElement;
/**
* Creates a new FormField with the variable name that uniquely identifies the field
@@ -219,6 +221,13 @@ public class FormField {
return variable;
}
+ /**
+ * @return the validateElement
+ */
+ public ValidateElement getValidateElement() {
+ return validateElement;
+ }
+
/**
* Sets a description that provides extra clarification about the question. This information
* could be presented to the user either in tool-tip, help button, or as a section of text
@@ -251,6 +260,14 @@ public class FormField {
this.required = required;
}
+ /**
+ * @param validateElement the validateElement to set
+ */
+ public void setValidateElement(ValidateElement validateElement) {
+ validateElement.checkConsistency(this);
+ this.validateElement = validateElement;
+ }
+
/**
* Sets an indicative of the format for the data to answer.
*
@@ -325,6 +342,7 @@ public class FormField {
for (Option option : getOptions()) {
buf.append(option.toXML());
}
+ buf.optElement(validateElement);
buf.closeElement(ELEMENT);
return buf;
}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java
index d9b13f79e..5c0184f9c 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/provider/DataFormProvider.java
@@ -25,6 +25,8 @@ import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.jivesoftware.smackx.xdatalayout.packet.DataLayout;
import org.jivesoftware.smackx.xdatalayout.provider.DataLayoutProvider;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.provider.DataValidationProvider;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -102,6 +104,7 @@ public class DataFormProvider extends PacketExtensionProvider {
switch (eventType) {
case XmlPullParser.START_TAG:
String name = parser.getName();
+ String namespace = parser.getNamespace();
switch (name) {
case "desc":
formField.setDescription(parser.nextText());
@@ -115,6 +118,12 @@ public class DataFormProvider extends PacketExtensionProvider {
case "option":
formField.addOption(parseOption(parser));
break;
+ // See XEP-122 Data Forms Validation
+ case ValidateElement.ELEMENT:
+ if (namespace.equals(ValidateElement.NAMESPACE)) {
+ formField.setValidateElement(DataValidationProvider.parse(parser));
+ }
+ break;
}
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/ValidationConsistencyException.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/ValidationConsistencyException.java
new file mode 100644
index 000000000..1b374484d
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/ValidationConsistencyException.java
@@ -0,0 +1,40 @@
+/**
+ *
+ * Copyright 2014 Anno van Vliet
+ *
+ * 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.xdatavalidation;
+
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;
+
+/**
+ * Exception thrown when {@link ValidateElement} is not consistent with the business rules in XEP=0122.
+ *
+ * @author Anno van Vliet
+ *
+ */
+public class ValidationConsistencyException extends IllegalArgumentException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param message
+ */
+ public ValidationConsistencyException(String message) {
+ super( message);
+ }
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/XDataValidationManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/XDataValidationManager.java
new file mode 100644
index 000000000..16429edcb
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/XDataValidationManager.java
@@ -0,0 +1,37 @@
+/**
+ *
+ * Copyright 2014 Anno van Vliet
+ *
+ * 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.xdatavalidation;
+
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPConnectionRegistry;
+import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;
+
+public class XDataValidationManager {
+
+ static {
+ XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
+ @Override
+ public void connectionCreated(XMPPConnection connection) {
+ ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
+ serviceDiscoveryManager.addFeature(ValidateElement.NAMESPACE);
+ }
+ });
+ }
+
+}
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/packet/ValidateElement.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/packet/ValidateElement.java
new file mode 100644
index 000000000..8d153512e
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/packet/ValidateElement.java
@@ -0,0 +1,436 @@
+/**
+ *
+ * Copyright 2014 Anno van Vliet
+ *
+ * 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.xdatavalidation.packet;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.PacketExtension;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+import org.jivesoftware.smackx.xdata.FormField;
+import org.jivesoftware.smackx.xdata.packet.DataForm;
+import org.jivesoftware.smackx.xdatavalidation.ValidationConsistencyException;
+
+/**
+ * DataValidation Extension according to XEP-0122: Data Forms Validation. This specification defines a
+ * backwards-compatible extension to the XMPP Data Forms protocol that enables applications to specify additional
+ * validation guidelines related to a {@link FormField} in a {@link DataForm}, such as validation of standard XML
+ * datatypes, application-specific datatypes, value ranges, and regular expressions.
+ *
+ * @author Anno van Vliet
+ */
+public abstract class ValidateElement implements PacketExtension {
+
+ public static final String DATATYPE_XS_STRING = "xs:string";
+ public static final String ELEMENT = "validate";
+ public static final String NAMESPACE = "http://jabber.org/protocol/xdata-validate";
+
+ private final String datatype;
+
+ private ListRange listRange;
+
+ /**
+ * The 'datatype' attribute specifies the datatype. This attribute is OPTIONAL, and when not specified, defaults to
+ * "xs:string".
+ *
+ * @param datatype the data type of any value contained within the {@link FormField} element.
+ */
+ private ValidateElement(String datatype) {
+ this.datatype = StringUtils.isNotEmpty(datatype) ? datatype : null;
+ }
+
+ /**
+ * Specifies the data type of any value contained within the {@link FormField} element. It MUST meet one of the
+ * following conditions:
+ *
+ * Start with "xs:", and be one of the "built-in" datatypes defined in XML Schema Part 2 [2]
+ * Start with a prefix registered with the XMPP Registrar [3]
+ * Start with "x:", and specify a user-defined datatype [4]
+ *
+ *
+ * @return the datatype
+ */
+ public String getDatatype() {
+ return datatype != null ? datatype : DATATYPE_XS_STRING;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ public XmlStringBuilder toXML() {
+ XmlStringBuilder buf = new XmlStringBuilder(this);
+ buf.optAttribute("datatype", datatype);
+ buf.rightAngleBracket();
+ appendXML(buf);
+ buf.optAppend(getListRange());
+ buf.closeElement(this);
+ return buf;
+ }
+
+ /**
+ * @param buf
+ */
+ protected abstract void appendXML(XmlStringBuilder buf);
+
+ /**
+ * @param listRange the listRange to set
+ */
+ public void setListRange(ListRange listRange) {
+ this.listRange = listRange;
+ }
+
+ /**
+ * @return the listRange
+ */
+ public ListRange getListRange() {
+ return listRange;
+ }
+
+ /**
+ * Check if this element is consistent according to the business rules in XEP=0122
+ *
+ * @param formField
+ */
+ public abstract void checkConsistency(FormField formField);
+
+ /**
+ *
+ * This defines the empty validate element that does only specify a 'datatype' attribute.
+ *
+ */
+ public static class EmptyValidateElement extends ValidateElement {
+
+ /**
+ * @param dataType
+ * @see #getDatatype()
+ */
+ public EmptyValidateElement(String dataType) {
+ super(dataType);
+ }
+
+ @Override
+ protected void appendXML(XmlStringBuilder buf) {
+ // The empty validate element does not contain any further elements or text, it is empty.
+ }
+
+ @Override
+ public void checkConsistency(FormField formField) {
+ // Since we can't know all possible datatypes, we can not perform any validation here
+ }
+ }
+
+ /**
+ * Validation only against the datatype itself. Indicates that the value(s) should simply match the field type and
+ * datatype constraints.
+ *
+ * @see ValidateElement
+ */
+ public static class BasicValidateElement extends ValidateElement {
+
+ public static final String METHOD = "basic";
+
+ /**
+ * @param dataType
+ * @see #getDatatype()
+ */
+ public BasicValidateElement(String dataType) {
+ super(dataType);
+ }
+
+ @Override
+ protected void appendXML(XmlStringBuilder buf) {
+ buf.emptyElement(METHOD);
+ }
+
+ public void checkConsistency(FormField formField) {
+ checkListRangeConsistency(formField);
+ if (formField.getType() != null) {
+ switch (formField.getType()) {
+ case hidden:
+ case jid_multi:
+ case jid_single:
+ throw new ValidationConsistencyException(String.format(
+ "Field type '%1$s' is not consistent with validation method '%2$s'.",
+ formField.getType(), BasicValidateElement.METHOD));
+ default:
+ break;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * For "list-single" or "list-multi", indicates that the user may enter a custom value (matching the datatype
+ * constraints) or choose from the predefined values.
+ *
+ * @see ValidateElement
+ */
+ public static class OpenValidateElement extends ValidateElement {
+
+ public static final String METHOD = "open";
+
+ /**
+ * @param dataType
+ * @see #getDatatype()
+ */
+ public OpenValidateElement(String dataType) {
+ super(dataType);
+ }
+
+ @Override
+ protected void appendXML(XmlStringBuilder buf) {
+ buf.emptyElement(METHOD);
+ }
+
+ public void checkConsistency(FormField formField) {
+ checkListRangeConsistency(formField);
+ if (formField.getType() != null) {
+ switch (formField.getType()) {
+ case hidden:
+ throw new ValidationConsistencyException(String.format(
+ "Field type '%1$s' is not consistent with validation method '%2$s'.",
+ formField.getType(), OpenValidateElement.METHOD));
+ default:
+ break;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Indicate that the value should fall within a certain range.
+ *
+ * @see ValidateElement
+ */
+ public static class RangeValidateElement extends ValidateElement {
+
+ public static final String METHOD = "range";
+ private final String min;
+ private final String max;
+
+ /**
+ * @param dataType
+ * @param min the minimum allowable value. This attribute is OPTIONAL. The value depends on the datatype in use.
+ * @param max the maximum allowable value. This attribute is OPTIONAL. The value depends on the datatype in use.
+ * @see #getDatatype()
+ *
+ */
+ public RangeValidateElement(String dataType, String min, String max) {
+ super(dataType);
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ protected void appendXML(XmlStringBuilder buf) {
+ buf.halfOpenElement(METHOD);
+ buf.optAttribute("min", getMin());
+ buf.optAttribute("max", getMax());
+ buf.closeEmptyElement();
+ }
+
+ /**
+ * The 'min' attribute specifies the minimum allowable value.
+ *
+ * @return the minimum allowable value. This attribute is OPTIONAL. The value depends on the datatype in use.
+ */
+ public String getMin() {
+ return min;
+ }
+
+ /**
+ * The 'max' attribute specifies the maximum allowable value.
+ *
+ * @return the maximum allowable value. This attribute is OPTIONAL. The value depends on the datatype in use.
+ */
+ public String getMax() {
+ return max;
+ }
+
+ public void checkConsistency(FormField formField) {
+ checkNonMultiConsistency(formField, METHOD);
+ if (getDatatype().equals(ValidateElement.DATATYPE_XS_STRING)) {
+ throw new ValidationConsistencyException(String.format(
+ "Field data type '%1$s' is not consistent with validation method '%2$s'.",
+ getDatatype(), RangeValidateElement.METHOD));
+ }
+ }
+
+ }
+
+ /**
+ * Indicates that the value should be restricted to a regular expression. The regular expression must be that
+ * defined for POSIX extended regular
+ * expressions including support for Unicode .
+ *
+ * @see ValidateElement
+ */
+ public static class RegexValidateElement extends ValidateElement {
+
+ public static final String METHOD = "regex";
+ private final String regex;
+
+ /**
+ * @param dataType
+ * @param regex
+ * @see #getDatatype()
+ */
+ public RegexValidateElement(String dataType, String regex) {
+ super(dataType);
+ this.regex = regex;
+ }
+
+ /**
+ * the expression is that defined for POSIX extended regular expressions, including support for Unicode.
+ *
+ * @return the regex
+ */
+ public String getRegex() {
+ return regex;
+ }
+
+ @Override
+ protected void appendXML(XmlStringBuilder buf) {
+ buf.element("regex", getRegex());
+ }
+
+ public void checkConsistency(FormField formField) {
+ checkNonMultiConsistency(formField, METHOD);
+ }
+
+ }
+
+ /**
+ * This element indicates for "list-multi", that a minimum and maximum number of options should be selected and/or
+ * entered.
+ */
+ public static class ListRange implements NamedElement {
+
+ public static final String ELEMENT = "list-range";
+ private final Long min;
+ private final Long max;
+
+ /**
+ * The 'max' attribute specifies the maximum allowable number of selected/entered values. The 'min' attribute
+ * specifies the minimum allowable number of selected/entered values. Both attributes are optional and must be a
+ * positive integer.
+ *
+ * @param min
+ * @param max
+ */
+ public ListRange(Long min, Long max) {
+ if (min != null && min < 0) {
+ throw new IllegalArgumentException("min must not be negative");
+ }
+ if (max != null && max < 0) {
+ throw new IllegalArgumentException("max must not be negative");
+ }
+ if (max == null && min == null) {
+ throw new IllegalArgumentException("Either min or max must be given");
+ }
+ this.min = min;
+ this.max = max;
+ }
+
+ public XmlStringBuilder toXML() {
+ XmlStringBuilder buf = new XmlStringBuilder(this);
+ buf.optLongAttribute("min", getMin());
+ buf.optLongAttribute("max", getMax());
+ buf.closeEmptyElement();
+ return buf;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ /**
+ * The minimum allowable number of selected/entered values.
+ *
+ * @return a positive integer, can be null
+ */
+ public Long getMin() {
+ return min;
+ }
+
+ /**
+ * The maximum allowable number of selected/entered values.
+ *
+ * @return a positive integer, can be null
+ */
+ public Long getMax() {
+ return max;
+ }
+
+ }
+
+ /**
+ * The element SHOULD be included only when the is of type "list-multi" and SHOULD be ignored
+ * otherwise.
+ *
+ * @param formField
+ */
+ protected void checkListRangeConsistency(FormField formField) {
+ ListRange listRange = getListRange();
+ if (listRange == null) {
+ return;
+ }
+
+ Long max = listRange.getMax();
+ Long min = listRange.getMin();
+ if ((max != null || min != null) && formField.getType() != FormField.Type.list_multi) {
+ throw new ValidationConsistencyException(
+ "Field type is not of type 'list-multi' while a 'list-range' is defined.");
+ }
+ }
+
+ /**
+ * @param formField
+ * @param method
+ */
+ protected void checkNonMultiConsistency(FormField formField, String method) {
+ checkListRangeConsistency(formField);
+ if (formField.getType() != null) {
+ switch (formField.getType()) {
+ case hidden:
+ case jid_multi:
+ case list_multi:
+ case text_multi:
+ throw new ValidationConsistencyException(String.format(
+ "Field type '%1$s' is not consistent with validation method '%2$s'.",
+ formField.getType(), method));
+ default:
+ break;
+ }
+ }
+ }
+}
+
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/provider/DataValidationProvider.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/provider/DataValidationProvider.java
new file mode 100644
index 000000000..a3a05100c
--- /dev/null
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdatavalidation/provider/DataValidationProvider.java
@@ -0,0 +1,95 @@
+/**
+ *
+ * Copyright 2014 Anno van Vliet
+ *
+ * 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.xdatavalidation.provider;
+
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import org.jivesoftware.smack.util.ParserUtils;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.BasicValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.EmptyValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.ListRange;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.OpenValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.RangeValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.RegexValidateElement;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Extension Provider for Data validation of forms.
+ *
+ * @author Anno van Vliet
+ *
+ */
+public class DataValidationProvider {
+ private static final Logger LOGGER = Logger.getLogger(DataValidationProvider.class.getName());
+
+ public static ValidateElement parse(XmlPullParser parser) throws XmlPullParserException, IOException {
+ final int initialDepth = parser.getDepth();
+ final String dataType = parser.getAttributeValue("", "datatype");
+ ValidateElement dataValidation = null;
+ ListRange listRange = null;
+
+ outerloop: while (true) {
+ int eventType = parser.next();
+ switch (eventType) {
+ case XmlPullParser.START_TAG:
+ switch (parser.getName()) {
+ case OpenValidateElement.METHOD:
+ dataValidation = new OpenValidateElement(dataType);
+ break;
+ case BasicValidateElement.METHOD:
+ dataValidation = new BasicValidateElement(dataType);
+ break;
+ case RangeValidateElement.METHOD:
+ dataValidation = new RangeValidateElement(dataType,
+ parser.getAttributeValue("", "min"),
+ parser.getAttributeValue("", "max")
+ );
+ break;
+ case RegexValidateElement.METHOD:
+ dataValidation = new RegexValidateElement(dataType,parser.nextText());
+ break;
+ case ListRange.ELEMENT:
+ Long min = ParserUtils.getLongAttribute(parser, "min");
+ Long max = ParserUtils.getLongAttribute(parser, "max");
+ if (min != null || max != null) {
+ listRange = new ListRange(min, max);
+ } else {
+ LOGGER.fine("Ignoring list-range element without min or max attribute");
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ if (parser.getDepth() == initialDepth) {
+ if (dataValidation == null) {
+ dataValidation = new EmptyValidateElement(dataType);
+ }
+ dataValidation.setListRange(listRange);
+ break outerloop;
+ }
+ break;
+ }
+ }
+ return dataValidation;
+ }
+
+}
diff --git a/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.xml b/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.xml
index 45ae5acb7..6974562fb 100644
--- a/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.xml
+++ b/smack-extensions/src/main/resources/org.jivesoftware.smackx/extensions.xml
@@ -14,5 +14,6 @@
org.jivesoftware.smackx.vcardtemp.VCardManager
org.jivesoftware.smackx.xdata.XDataManager
org.jivesoftware.smackx.xdatalayout.XDataLayoutManager
+ org.jivesoftware.smackx.xdatavalidation.XDataValidationManager
diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/packet/DataFormTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/packet/DataFormTest.java
index eafb055bc..d21a3cf8e 100644
--- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/packet/DataFormTest.java
+++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdata/packet/DataFormTest.java
@@ -20,8 +20,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
-import java.io.StringReader;
-import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.packet.Element;
@@ -32,6 +30,9 @@ import org.jivesoftware.smackx.xdatalayout.packet.DataLayout;
import org.jivesoftware.smackx.xdatalayout.packet.DataLayout.Fieldref;
import org.jivesoftware.smackx.xdatalayout.packet.DataLayout.Section;
import org.jivesoftware.smackx.xdatalayout.packet.DataLayout.Text;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.RangeValidateElement;
+
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -45,11 +46,10 @@ import org.xmlpull.v1.XmlPullParserException;
public class DataFormTest {
private static final String TEST_OUTPUT_1 = "InstructionTest1 ";
private static final String TEST_OUTPUT_2 = "InstructionTest1 PageText ";
- private static Logger logger = Logger.getLogger(DataFormTest.class.getName());
+ private static final String TEST_OUTPUT_3 = "InstructionTest1 ";
@Test
public void test() throws XmlPullParserException, IOException, SmackException {
-
//Build a Form
DataForm df = new DataForm("SUBMIT");
String instruction = "InstructionTest1";
@@ -59,12 +59,11 @@ public class DataFormTest {
assertNotNull( df.toXML());
String output = df.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_1, output);
DataFormProvider pr = new DataFormProvider();
- XmlPullParser parser = getParser(output);
+ XmlPullParser parser = PacketParserUtils.getParserFor(output);
df = pr.parse(parser);
@@ -75,15 +74,11 @@ public class DataFormTest {
assertNotNull( df.toXML());
output = df.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_1, output);
-
-
}
@Test
public void testLayout() throws XmlPullParserException, IOException, SmackException {
-
//Build a Form
DataForm df = new DataForm("SUBMIT");
String instruction = "InstructionTest1";
@@ -104,12 +99,11 @@ public class DataFormTest {
assertNotNull( df.toXML());
String output = df.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_2, output);
DataFormProvider pr = new DataFormProvider();
- XmlPullParser parser = getParser(output);
+ XmlPullParser parser = PacketParserUtils.getParserFor(output);
df = pr.parse(parser);
@@ -124,23 +118,42 @@ public class DataFormTest {
assertNotNull( df.toXML());
output = df.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_2, output);
-
-
}
- /**
- * @param output
- * @return
- * @throws XmlPullParserException
- * @throws IOException
- */
- private XmlPullParser getParser(String output) throws XmlPullParserException, IOException {
- logger.finest("getParser");
- XmlPullParser parser = PacketParserUtils.newXmppParser();
- parser.setInput(new StringReader(output));
- parser.next();
- return parser;
+ @Test
+ public void testValidation() throws XmlPullParserException, IOException, SmackException {
+ //Build a Form
+ DataForm df = new DataForm("SUBMIT");
+ String instruction = "InstructionTest1";
+ df.addInstruction(instruction);
+ FormField field = new FormField("testField1");
+ df.addField(field);
+
+ ValidateElement dv = new RangeValidateElement("xs:integer","1111", "9999");
+ field.setValidateElement(dv);
+
+ assertNotNull( df.toXML());
+ String output = df.toXML().toString();
+ assertEquals(TEST_OUTPUT_3, output);
+
+ DataFormProvider pr = new DataFormProvider();
+
+ XmlPullParser parser = PacketParserUtils.getParserFor(output);
+
+ df = pr.parse(parser);
+
+ assertNotNull(df);
+ assertNotNull(df.getFields());
+ assertEquals(1 , df.getFields().size() );
+ Element element = df.getFields().get(0).getValidateElement();
+ assertNotNull(element);
+ dv = (ValidateElement) element;
+
+ assertEquals("xs:integer" , dv.getDatatype());
+
+ assertNotNull( df.toXML());
+ output = df.toXML().toString();
+ assertEquals(TEST_OUTPUT_3, output);
}
}
diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatalayout/packet/DataLayoutTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatalayout/packet/DataLayoutTest.java
index 0ac475b0d..dff43d1a6 100644
--- a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatalayout/packet/DataLayoutTest.java
+++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatalayout/packet/DataLayoutTest.java
@@ -21,8 +21,6 @@ import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.io.InputStreamReader;
-import java.io.StringReader;
-import java.util.logging.Logger;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.util.PacketParserUtils;
@@ -47,12 +45,9 @@ public class DataLayoutTest {
private static final String TEST_OUTPUT_2 = "PageText ";
private static final String TEST_OUTPUT_SPECIAL = "SectionText - & \u00E9 \u00E1 PageText - & \u00E9 \u00E1 <html><font color='red'><em>DO NOT DELAY</em></font><br/>supply further information</html> ";
private static final String TEST_INPUT_1 = "xdata-layout-sample.xml";
- private static Logger logger = Logger.getLogger(DataLayoutTest.class.getName());
-
@Test
public void testLayout() throws XmlPullParserException, IOException, SmackException {
-
DataLayout layout = new DataLayout("Label");
Fieldref reffield = new Fieldref("testField1");
layout.getPageLayout().add(reffield);
@@ -63,10 +58,9 @@ public class DataLayoutTest {
assertNotNull( layout.toXML());
String output = layout.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_2, output);
- XmlPullParser parser = getParser(output);
+ XmlPullParser parser = PacketParserUtils.getParserFor(output);
layout = DataLayoutProvider.parse(parser);
@@ -75,10 +69,7 @@ public class DataLayoutTest {
assertNotNull( layout.toXML());
output = layout.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_2, output);
-
-
}
@Test
@@ -101,10 +92,9 @@ public class DataLayoutTest {
assertNotNull( layout.toXML());
String output = layout.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_SPECIAL, output);
- XmlPullParser parser = getParser(output);
+ XmlPullParser parser = PacketParserUtils.getParserFor(output);
layout = DataLayoutProvider.parse(parser);
@@ -122,16 +112,11 @@ public class DataLayoutTest {
assertNotNull( layout.toXML());
output = layout.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_SPECIAL, output);
-
-
}
@Test
public void testLayoutFromFile() throws XmlPullParserException, IOException, SmackException {
-
-
DataFormProvider pr = new DataFormProvider();
XmlPullParser parser = PacketParserUtils.newXmppParser();
@@ -158,24 +143,6 @@ public class DataLayoutTest {
assertNotNull( layout.toXML());
String output = layout.toXML().toString();
- logger.finest(output);
assertEquals(TEST_OUTPUT_SPECIAL, output);
-
-
- }
-
-
- /**
- * @param output
- * @return
- * @throws XmlPullParserException
- * @throws IOException
- */
- private XmlPullParser getParser(String output) throws XmlPullParserException, IOException {
- logger.finest("getParser");
- XmlPullParser parser = PacketParserUtils.newXmppParser();
- parser.setInput(new StringReader(output));
- parser.next();
- return parser;
}
}
diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/DataValidationHelperTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/DataValidationHelperTest.java
new file mode 100644
index 000000000..3f28e8dde
--- /dev/null
+++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/DataValidationHelperTest.java
@@ -0,0 +1,120 @@
+/**
+ *
+ * Copyright 2014 Anno van Vliet
+ *
+ * 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.xdatavalidation;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.jivesoftware.smackx.xdata.FormField;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.BasicValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.ListRange;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.OpenValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.RangeValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.RegexValidateElement;
+import org.junit.Test;
+
+/**
+ *
+ * @author Anno van Vliet
+ *
+ */
+public class DataValidationHelperTest {
+
+
+ @Test
+ public void testCheckConsistencyFormFieldBasicValidateElement() {
+ FormField field = new FormField("var");
+ field.setType(FormField.Type.jid_single);
+ BasicValidateElement element = new BasicValidateElement(null);
+ try {
+ element.checkConsistency(field);
+ fail("No correct check on consistency");
+ }
+ catch (ValidationConsistencyException e) {
+ assertEquals("Field type 'jid-single' is not consistent with validation method 'basic'.", e.getMessage());
+ }
+
+ try {
+ new ListRange(-1L, 1L);
+ fail("No correct check on consistency");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("min must not be negative", e.getMessage());
+ }
+
+ element.setListRange(new ListRange(10L, 100L));
+ try {
+ element.checkConsistency(field);
+ fail("No correct check on consistency");
+ }
+ catch (ValidationConsistencyException e) {
+ assertEquals("Field type is not of type 'list-multi' while a 'list-range' is defined.", e.getMessage());
+ }
+
+ field.setType(FormField.Type.list_multi);
+ try {
+ element.checkConsistency(field);
+ }
+ catch (ValidationConsistencyException e) {
+ fail("No correct check on consistency");
+ }
+ }
+
+
+ @Test
+ public void testCheckConsistencyFormFieldOpenValidateElement() {
+ FormField field = new FormField("var");
+ field.setType(FormField.Type.hidden);
+ OpenValidateElement element = new OpenValidateElement(null);
+ try {
+ element.checkConsistency(field);
+ fail("No correct check on consistency");
+ }
+ catch (ValidationConsistencyException e) {
+ assertEquals("Field type 'hidden' is not consistent with validation method 'open'.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testCheckConsistencyFormFieldRangeValidateElement() {
+ FormField field = new FormField("var");
+ field.setType(FormField.Type.text_multi);
+ RangeValidateElement element = new RangeValidateElement("xs:integer",null, "99");
+ try {
+ element.checkConsistency(field);
+ fail("No correct check on consistency");
+ }
+ catch (ValidationConsistencyException e) {
+ assertEquals("Field type 'text-multi' is not consistent with validation method 'range'.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testCheckConsistencyFormFieldRegexValidateElement() {
+ FormField field = new FormField("var");
+ field.setType(FormField.Type.list_multi);
+ RegexValidateElement element = new RegexValidateElement(null, ".*");
+ try {
+ element.checkConsistency(field);
+ fail("No correct check on consistency");
+ }
+ catch (ValidationConsistencyException e) {
+ assertEquals("Field type 'list-multi' is not consistent with validation method 'regex'.", e.getMessage());
+ }
+ }
+}
diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/provider/DataValidationTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/provider/DataValidationTest.java
new file mode 100644
index 000000000..fa15d2f9b
--- /dev/null
+++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/xdatavalidation/provider/DataValidationTest.java
@@ -0,0 +1,140 @@
+/**
+ *
+ * Copyright 2014 Anno van Vliet
+ *
+ * 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.xdatavalidation.provider;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.test.util.TestUtils;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.EmptyValidateElement;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.ListRange;
+import org.jivesoftware.smackx.xdatavalidation.packet.ValidateElement.RangeValidateElement;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ *
+ * @author Anno van Vliet
+ *
+ */
+public class DataValidationTest {
+ private static final String TEST_OUTPUT_MIN = " ";
+ private static final String TEST_OUTPUT_RANGE = " ";
+ private static final String TEST_OUTPUT_RANGE2 = " ";
+ private static final String TEST_OUTPUT_FAIL = " ";
+
+ @Test
+ public void testMin() throws XmlPullParserException, IOException, SmackException {
+
+ ValidateElement dv = new EmptyValidateElement(null);
+
+ assertNotNull( dv.toXML());
+ String output = dv.toXML().toString();
+ assertEquals(TEST_OUTPUT_MIN, output);
+
+ XmlPullParser parser = getParser(output);
+
+ dv = DataValidationProvider.parse(parser);
+
+ assertNotNull(dv);
+ assertEquals("xs:string", dv.getDatatype());
+ assertTrue( dv instanceof EmptyValidateElement);
+
+ assertNotNull( dv.toXML());
+ output = dv.toXML().toString();
+ assertEquals(TEST_OUTPUT_MIN, output);
+ }
+
+ @Test
+ public void testRange() throws XmlPullParserException, IOException, SmackException {
+
+ ValidateElement dv = new RangeValidateElement("xs:string", "min-val", "max-val");
+
+ ListRange listRange = new ListRange(111L, 999L);
+ dv.setListRange(listRange );
+
+ assertNotNull( dv.toXML());
+ String output = dv.toXML().toString();
+ assertEquals(TEST_OUTPUT_RANGE, output);
+
+ XmlPullParser parser = getParser(output);
+
+ dv = DataValidationProvider.parse(parser);
+
+ assertNotNull(dv);
+ assertEquals("xs:string", dv.getDatatype());
+ assertTrue(dv instanceof RangeValidateElement );
+ RangeValidateElement rdv = (RangeValidateElement) dv;
+ assertEquals("min-val", rdv.getMin());
+ assertEquals("max-val", rdv.getMax());
+ assertNotNull(rdv.getListRange());
+ assertEquals(new Long(111), rdv.getListRange().getMin());
+ assertEquals(999, rdv.getListRange().getMax().intValue());
+
+
+ assertNotNull( dv.toXML());
+ output = dv.toXML().toString();
+ assertEquals(TEST_OUTPUT_RANGE, output);
+ }
+
+ @Test
+ public void testRange2() throws XmlPullParserException, IOException, SmackException {
+
+ ValidateElement dv = new RangeValidateElement(null, null, null);
+
+ assertNotNull( dv.toXML());
+ String output = dv.toXML().toString();
+ assertEquals(TEST_OUTPUT_RANGE2, output);
+
+ XmlPullParser parser = getParser(output);
+
+ dv = DataValidationProvider.parse(parser);
+
+ assertNotNull(dv);
+ assertEquals("xs:string", dv.getDatatype());
+ assertTrue(dv instanceof RangeValidateElement );
+ RangeValidateElement rdv = (RangeValidateElement) dv;
+ assertEquals(null, rdv.getMin());
+ assertEquals(null, rdv.getMax());
+
+ assertNotNull( rdv.toXML());
+ output = rdv.toXML().toString();
+ assertEquals(TEST_OUTPUT_RANGE2, output);
+ }
+
+ @Test(expected=NumberFormatException.class)
+ public void testRangeFailure() throws IOException, SmackException, XmlPullParserException {
+ XmlPullParser parser = getParser(TEST_OUTPUT_FAIL);
+ DataValidationProvider.parse(parser);
+ }
+
+ /**
+ * @param output
+ * @return
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private XmlPullParser getParser(String output) throws XmlPullParserException, IOException {
+ return TestUtils.getParser(output, "validate");
+ }
+}