Merge branch '4.2'

This commit is contained in:
Florian Schmaus 2018-02-22 14:54:48 +01:00
commit 026f3a2f8c
34 changed files with 2250 additions and 3 deletions

View File

@ -93,11 +93,14 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
| [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. |
| Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. |
| [Push Notifications](pushnotifications.md) | [XEP-0357](http://xmpp.org/extensions/xep-0357.html) | Defines a way to manage push notifications from an XMPP Server. |
| Stable and Unique Stanza IDs | [XEP-0359](http://xmpp.org/extensions/xep-0359.html) | This specification describes unique and stable IDs for messages. |
| HTTP File Upload | [XEP-0363](http://xmpp.org/extensions/xep-0363.html) | Protocol to request permissions to upload a file to an HTTP server and get a shareable URL. |
| [Spoiler Messages](spoiler.md) | [XEP-0382](http://xmpp.org/extensions/xep-0382.html) | Indicate that the body of a message should be treated as a spoiler |
| [Multi-User Chat Light](muclight.md) | [XEP-xxxx](http://mongooseim.readthedocs.io/en/latest/open-extensions/xeps/xep-muc-light.html) | Multi-User Chats for mobile XMPP applications and specific enviroment. |
| [OMEMO Multi End Message and Object Encryption](omemo.md) | [XEP-XXXX](https://conversations.im/omemo/xep-omemo.html) | Encrypt messages using OMEMO encryption (currently only with smack-omemo-signal -> GPLv3). |
| [Consistent Color Generation](consistent_colors.md) | [XEP-0392](http://xmpp.org/extensions/xep-0392.html) | Generate consistent colors for identifiers like usernames to provide a consistent user experience. |
| Google GCM JSON payload | n/a | Semantically the same as XEP-0335: JSON Containers |
| [Message Markup](messagemarkup.md) | [XEP-0394](http://xmpp.org/extensions/xep-0394.html)| Style message bodies while keeping body and markup information separated. |
Legacy Smack Extensions and currently supported XEPs of smack-legacy

View File

@ -0,0 +1,68 @@
Message Markup
==============
[Back](index.md)
[Message Markup (XEP-0394)](https://xmpp.org/extensions/xep-0394.html) can be used as a an alternative to XHTML-IM to style messages, while keeping the body and markup information strictly separated.
This implementation can *not* be used to render message bodies, but will offer a simple to use interface for creating ExtensionElements which encode the markup information.
## Usage
The most important class is the `MarkupElement` class, which contains a Builder.
To start creating a Message Markup Extension, call `MarkupElement.getBuilder()`.
(Almost) all method calls documented below will be made on the builder.
Whenever a method call receives a `start` and `end` index, `start` represents the first character, which is affected by the styling, while `end` is the character *after* the last affected character.
### Inline styling
Currently there are 3 styles available:
* *emphasis*, which should be rendered by a client as *italic*, or **bold**
* *code*, which should be rendered in `monospace`
* *deleted*, which should be rendered as ~~strikethrough~~.
Those styles are available by calling `builder.setEmphasis(int start, int end)`,
`builder.setDeleted(int start, int end)` and `builder.setCode(int start, int end)`.
If you want to apply multiple inline styles to a section, you can do the following:
```
Set<SpanElement.SpanStyle> spanStyles = new HashSet<>();
styles.add(SpanElement.SpanStyle.emphasis);
styles.add(SpanElement.SpanStyle.deleted);
builder.addSpan(start, end, spanStyles);
```
Note, that spans cannot overlap one another.
### Block Level Styling
Available block level styles are:
* Code blocks, which should be rendered as
```
blocks
of
code
```
* Itemized lists, which should render as
* Lists
* with possibly multiple
* entries
* Block Quotes, which should be rendered by the client
> as quotes, which
>> also can be nested
To mark a section as code block, call `builder.setCodeBlock(start, end)`.
To create a list, call `MarkupElement.Builder.ListBuilder lbuilder = builder.beginList()`, which will return a list builder.
On this you can call `lbuilder.addEntry(start, end)` to add an entry.
Note: If you add an entry, the start value MUST be equal to the end value of the previous added entry!
To end the list, call `lbuilder.endList()`, which will return the MessageMarkup builder.
To create a block quote, call `builder.setBlockQuote(start, end)`.
Note that block level elements MUST NOT overlap each other boundaries, but may be fully contained (nested) within each other.

View File

@ -0,0 +1,33 @@
Spoiler Messages
================
[Back](index.md)
Spoiler Messages can be used to indicate that the body of a message is a spoiler and should be displayed as such.
## Usage
To get an instance of the SpoilerManager, call
```
SpoilerManager manager = SpoilerManager.getInstanceFor(connection);
```
This will automatically add Spoilers to the list of supported features of your client.
The manager can then be used to add SpoilerElements to messages like follows:
```
Message message = new Message();
// spoiler without hint
SpoilerElement.addSpoiler(message);
// spoiler with hint about content
SpoilerElement.addSpoiler(message, "End of Love Story");
// spoiler with localized hint
SpoilerElement.addSpoiler(message, "de", "Der Kuchen ist eine Lüge");
```
To get Spoilers from a message call
```
Map<String, String> spoilers = SpoilerElement.getSpoilers(message);
```

View File

@ -0,0 +1,62 @@
/**
*
* Copyright © 2018 Paul Schaub
*
* 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.message_markup.element;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class BlockQuoteElement implements MarkupElement.BlockLevelMarkupElement {
public static final String ELEMENT = "bquote";
private final int start, end;
/**
* Create a new Block Quote element.
*
* @param start start index
* @param end end index
*/
public BlockQuoteElement(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public int getStart() {
return start;
}
@Override
public int getEnd() {
return end;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(this);
xml.attribute(ATTR_START, getStart());
xml.attribute(ATTR_END, getEnd());
xml.closeEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,62 @@
/**
*
* Copyright © 2018 Paul Schaub
*
* 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.message_markup.element;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class CodeBlockElement implements MarkupElement.BlockLevelMarkupElement {
public static final String ELEMENT = "bcode";
private final int start, end;
/**
* Create a new Code Block element.
*
* @param start start index
* @param end end index
*/
public CodeBlockElement(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public int getStart() {
return start;
}
@Override
public int getEnd() {
return end;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(this);
xml.attribute(ATTR_START, getStart());
xml.attribute(ATTR_END, getEnd());
xml.closeEmptyElement();
return xml;
}
}

View File

@ -0,0 +1,121 @@
/**
*
* Copyright © 2018 Paul Schaub
*
* 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.message_markup.element;
import java.util.Collections;
import java.util.List;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class ListElement implements MarkupElement.MarkupChildElement {
public static final String ELEMENT = "list";
public static final String ELEM_LI = "li";
private final int start, end;
private final List<ListEntryElement> entries;
/**
* Create a new List element.
*
* @param start start index of the list
* @param end end index of the list
* @param entries list entries
*/
public ListElement(int start, int end, List<ListEntryElement> entries) {
this.start = start;
this.end = end;
this.entries = Collections.unmodifiableList(entries);
}
@Override
public int getStart() {
return start;
}
@Override
public int getEnd() {
return end;
}
/**
* Return a list of all list entries.
*
* @return entries
*/
public List<ListEntryElement> getEntries() {
return entries;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public CharSequence toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(this);
xml.attribute(ATTR_START, getStart());
xml.attribute(ATTR_END, getEnd());
xml.rightAngleBracket();
for (ListEntryElement li : getEntries()) {
xml.append(li.toXML());
}
xml.closeElement(this);
return xml;
}
public static class ListEntryElement implements NamedElement {
private final int start;
/**
* Create a new ListEntry element.
*
* @param start start index
*/
public ListEntryElement(int start) {
this.start = start;
}
/**
* Return the start index of this entry.
* @return start index
*/
public int getStart() {
return start;
}
@Override
public String getElementName() {
return ELEM_LI;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(this);
xml.attribute(ATTR_START, getStart());
xml.closeEmptyElement();
return xml;
}
}
}

View File

@ -0,0 +1,312 @@
/**
*
* Copyright © 2018 Paul Schaub
*
* 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.message_markup.element;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class MarkupElement implements ExtensionElement {
public static final String NAMESPACE = "urn:xmpp:markup:0";
public static final String ELEMENT = "markup";
private final List<MarkupChildElement> childElements;
/**
* Create a new MarkupElement.
*
* @param childElements child elements.
*/
public MarkupElement(List<MarkupChildElement> childElements) {
this.childElements = Collections.unmodifiableList(childElements);
}
/**
* Return a new Builder for Message Markup elements.
* @return builder.
*/
public static Builder getBuilder() {
return new Builder();
}
/**
* Return a list of all child elements.
* @return children
*/
public List<MarkupChildElement> getChildElements() {
return childElements;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this).rightAngleBracket();
for (MarkupChildElement child : getChildElements()) {
xml.append(child.toXML());
}
xml.closeElement(this);
return xml;
}
public static final class Builder {
private final List<SpanElement> spans = new ArrayList<>();
private final List<BlockQuoteElement> quotes = new ArrayList<>();
private final List<CodeBlockElement> codes = new ArrayList<>();
private final List<ListElement> lists = new ArrayList<>();
private Builder() {
}
/**
* Mark a section of a message as deleted.
*
* @param start start index
* @param end end index
* @return builder
*/
public Builder setDeleted(int start, int end) {
return addSpan(start, end, Collections.singleton(SpanElement.SpanStyle.deleted));
}
/**
* Mark a section of a message as emphasized.
*
* @param start start index
* @param end end index
* @return builder
*/
public Builder setEmphasis(int start, int end) {
return addSpan(start, end, Collections.singleton(SpanElement.SpanStyle.emphasis));
}
/**
* Mark a section of a message as inline code.
*
* @param start start index
* @param end end index
* @return builder
*/
public Builder setCode(int start, int end) {
return addSpan(start, end, Collections.singleton(SpanElement.SpanStyle.code));
}
/**
* Add a span element.
*
* @param start start index
* @param end end index
* @param styles list of text styles for that span
* @return builder
*/
public Builder addSpan(int start, int end, Set<SpanElement.SpanStyle> styles) {
verifyStartEnd(start, end);
for (SpanElement other : spans) {
if ((start >= other.getStart() && start <= other.getEnd()) ||
(end >= other.getStart() && end <= other.getEnd())) {
throw new IllegalArgumentException("Spans MUST NOT overlap each other.");
}
}
spans.add(new SpanElement(start, end, styles));
return this;
}
/**
* Mark a section of a message as block quote.
*
* @param start start index
* @param end end index
* @return builder
*/
public Builder setBlockQuote(int start, int end) {
verifyStartEnd(start, end);
for (BlockQuoteElement other : quotes) {
// 1 if out, 0 if on, -1 if in
Integer s = start;
Integer e = end;
int startPos = s.compareTo(other.getStart()) * s.compareTo(other.getEnd());
int endPos = e.compareTo(other.getStart()) * e.compareTo(other.getEnd());
int allowed = startPos * endPos;
if (allowed < 1) {
throw new IllegalArgumentException("BlockQuotes MUST NOT overlap each others boundaries");
}
}
quotes.add(new BlockQuoteElement(start, end));
return this;
}
/**
* Mark a section of a message as a code block.
*
* @param start start index
* @param end end index
* @return builder
*/
public Builder setCodeBlock(int start, int end) {
verifyStartEnd(start, end);
codes.add(new CodeBlockElement(start, end));
return this;
}
/**
* Begin a list.
*
* @return list builder
*/
public Builder.ListBuilder beginList() {
return new Builder.ListBuilder(this);
}
public static final class ListBuilder {
private final Builder markup;
private final ArrayList<ListElement.ListEntryElement> entries = new ArrayList<>();
private int end = -1;
private ListBuilder(Builder markup) {
this.markup = markup;
}
/**
* Add an entry to the list.
* The start index of an entry must correspond to the end index of the previous entry
* (if a previous entry exists.)
*
* @param start start index
* @param end end index
* @return list builder
*/
public Builder.ListBuilder addEntry(int start, int end) {
verifyStartEnd(start, end);
ListElement.ListEntryElement last = entries.size() == 0 ? null : entries.get(entries.size() - 1);
// Entries themselves do not store end values, that's why we store the last entries end value in this.end
if (last != null && start != this.end) {
throw new IllegalArgumentException("Next entries start must be equal to last entries end (" + this.end + ").");
}
entries.add(new ListElement.ListEntryElement(start));
this.end = end;
return this;
}
/**
* End the list.
*
* @return builder
*/
public Builder endList() {
if (entries.size() > 0) {
ListElement.ListEntryElement first = entries.get(0);
ListElement list = new ListElement(first.getStart(), end, entries);
markup.lists.add(list);
}
return markup;
}
}
/**
* Build a Message Markup element.
*
* @return extension element
*/
public MarkupElement build() {
List<MarkupElement.MarkupChildElement> children = new ArrayList<>();
children.addAll(spans);
children.addAll(quotes);
children.addAll(codes);
children.addAll(lists);
return new MarkupElement(children);
}
private static void verifyStartEnd(int start, int end) {
if (start >= end || start < 0) {
throw new IllegalArgumentException("Start value (" + start + ") MUST be greater equal than 0 " +
"and MUST be smaller than end value (" + end + ").");
}
}
}
/**
* Interface for child elements.
*/
public interface MarkupChildElement extends NamedElement {
String ATTR_START = "start";
String ATTR_END = "end";
/**
* Return the start index of this element.
*
* @return start index
*/
int getStart();
/**
* Return the end index of this element.
*
* @return end index
*/
int getEnd();
}
/**
* Interface for block level child elements.
*/
public interface BlockLevelMarkupElement extends MarkupChildElement {
}
}

View File

@ -0,0 +1,93 @@
/**
*
* Copyright © 2018 Paul Schaub
*
* 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.message_markup.element;
import java.util.Collections;
import java.util.Set;
import org.jivesoftware.smack.util.XmlStringBuilder;
public class SpanElement implements MarkupElement.MarkupChildElement {
public static final String ELEMENT = "span";
private final int start, end;
private final Set<SpanStyle> styles;
/**
* Create a new Span element.
*
* @param start start index
* @param end end index
* @param styles list of styles that apply to this span
*/
public SpanElement(int start, int end, Set<SpanStyle> styles) {
this.start = start;
this.end = end;
this.styles = Collections.unmodifiableSet(styles);
}
@Override
public int getStart() {
return start;
}
@Override
public int getEnd() {
return end;
}
/**
* Return all styles of this span.
*
* @return styles
*/
public Set<SpanStyle> getStyles() {
return styles;
}
public static final String emphasis = "emphasis";
public static final String code = "code";
public static final String deleted = "deleted";
public enum SpanStyle {
emphasis,
code,
deleted
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML() {
XmlStringBuilder xml = new XmlStringBuilder();
xml.halfOpenElement(this);
xml.attribute(ATTR_START, getStart());
xml.attribute(ATTR_END, getEnd());
xml.rightAngleBracket();
for (SpanStyle style : getStyles()) {
xml.halfOpenElement(style.toString()).closeEmptyElement();
}
xml.closeElement(this);
return xml;
}
}

View File

@ -0,0 +1,25 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* XEP-0394: Message Markup.
*
* @see <a href="http://xmpp.org/extensions/xep-0394.html">XEP-0394: Message
* Markup</a>
*
*/
package org.jivesoftware.smackx.message_markup.element;

View File

@ -0,0 +1,25 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* XEP-0394: Message Markup.
*
* @see <a href="http://xmpp.org/extensions/xep-0394.html">XEP-0394: Message
* Markup</a>
*
*/
package org.jivesoftware.smackx.message_markup;

View File

@ -0,0 +1,139 @@
/**
*
* Copyright © 2018 Paul Schaub
*
* 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.message_markup.provider;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.message_markup.element.BlockQuoteElement;
import org.jivesoftware.smackx.message_markup.element.CodeBlockElement;
import org.jivesoftware.smackx.message_markup.element.ListElement;
import org.jivesoftware.smackx.message_markup.element.MarkupElement;
import org.jivesoftware.smackx.message_markup.element.SpanElement;
import org.xmlpull.v1.XmlPullParser;
public class MarkupElementProvider extends ExtensionElementProvider<MarkupElement> {
@Override
public MarkupElement parse(XmlPullParser parser, int initialDepth) throws Exception {
MarkupElement.Builder markup = MarkupElement.getBuilder();
int spanStart = -1, spanEnd = -1;
Set<SpanElement.SpanStyle> spanStyles = new HashSet<>();
int listStart = -1, listEnd = -1;
List<ListElement.ListEntryElement> lis = new ArrayList<>();
while (true) {
int tag = parser.next();
String name = parser.getName();
int start, end;
switch (tag) {
case START_TAG:
switch (name) {
case BlockQuoteElement.ELEMENT:
start = ParserUtils.getIntegerAttributeOrThrow(parser, BlockQuoteElement.ATTR_START,
"Message Markup BlockQuoteElement MUST contain a 'start' attribute.");
end = ParserUtils.getIntegerAttributeOrThrow(parser, BlockQuoteElement.ATTR_END,
"Message Markup BlockQuoteElement MUST contain a 'end' attribute.");
markup.setBlockQuote(start, end);
break;
case CodeBlockElement.ELEMENT:
start = ParserUtils.getIntegerAttributeOrThrow(parser, CodeBlockElement.ATTR_START,
"Message Markup CodeBlockElement MUST contain a 'start' attribute.");
end = ParserUtils.getIntegerAttributeOrThrow(parser, CodeBlockElement.ATTR_END,
"Message Markup CodeBlockElement MUST contain a 'end' attribute.");
markup.setCodeBlock(start, end);
break;
case SpanElement.ELEMENT:
spanStyles = new HashSet<>();
spanStart = ParserUtils.getIntegerAttributeOrThrow(parser, SpanElement.ATTR_START,
"Message Markup SpanElement MUST contain a 'start' attribute.");
spanEnd = ParserUtils.getIntegerAttributeOrThrow(parser, SpanElement.ATTR_END,
"Message Markup SpanElement MUST contain a 'end' attribute.");
break;
case SpanElement.code:
spanStyles.add(SpanElement.SpanStyle.code);
break;
case SpanElement.emphasis:
spanStyles.add(SpanElement.SpanStyle.emphasis);
break;
case SpanElement.deleted:
spanStyles.add(SpanElement.SpanStyle.deleted);
break;
case ListElement.ELEMENT:
lis = new ArrayList<>();
listStart = ParserUtils.getIntegerAttributeOrThrow(parser, ListElement.ATTR_START,
"Message Markup ListElement MUST contain a 'start' attribute.");
listEnd = ParserUtils.getIntegerAttributeOrThrow(parser, ListElement.ATTR_END,
"Message Markup ListElement MUST contain a 'end' attribute.");
break;
case ListElement.ELEM_LI:
start = ParserUtils.getIntegerAttributeOrThrow(parser, ListElement.ATTR_START,
"Message Markup ListElement 'li' MUST contain a 'start' attribute.");
lis.add(new ListElement.ListEntryElement(start));
break;
}
break;
case END_TAG:
switch (name) {
case SpanElement.ELEMENT:
markup.addSpan(spanStart, spanEnd, spanStyles);
spanStart = -1; spanEnd = -1;
break;
case ListElement.ELEMENT:
MarkupElement.Builder.ListBuilder listBuilder = markup.beginList();
if (lis.size() > 0 && lis.get(0).getStart() != listStart) {
throw new SmackException("Error while parsing incoming MessageMarkup ListElement: " +
"'start' attribute of first 'li' element must equal 'start' attribute of list.");
}
for (int i = 0; i < lis.size(); i++) {
int elemStart = lis.get(i).getStart();
int elemEnd = i < lis.size() - 1 ? lis.get(i + 1).getStart() : listEnd;
listBuilder.addEntry(elemStart, elemEnd);
}
listBuilder.endList();
break;
case MarkupElement.ELEMENT:
return markup.build();
}
}
}
}
}

View File

@ -0,0 +1,25 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* XEP-0394: Message Markup.
*
* @see <a href="http://xmpp.org/extensions/xep-0394.html">XEP-0394: Message
* Markup</a>
*
*/
package org.jivesoftware.smackx.message_markup.provider;

View File

@ -0,0 +1,118 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.sid;
import java.util.Map;
import java.util.WeakHashMap;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.sid.element.OriginIdElement;
public final class StableUniqueStanzaIdManager extends Manager {
public static final String NAMESPACE = "urn:xmpp:sid:0";
private static final Map<XMPPConnection, StableUniqueStanzaIdManager> INSTANCES = new WeakHashMap<>();
// Filter for outgoing stanzas.
private static final StanzaFilter OUTGOING_FILTER = new AndFilter(
MessageTypeFilter.NORMAL_OR_CHAT_OR_HEADLINE,
ToTypeFilter.ENTITY_FULL_OR_BARE_JID);
private static final StanzaFilter ORIGIN_ID_FILTER = new StanzaExtensionFilter(OriginIdElement.ELEMENT, NAMESPACE);
// Listener for outgoing stanzas that adds origin-ids to outgoing stanzas.
private final StanzaListener stanzaListener = new StanzaListener() {
@Override
public void processStanza(Stanza stanza) {
OriginIdElement.addOriginId((Message) stanza);
}
};
static {
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
@Override
public void connectionCreated(XMPPConnection connection) {
getInstanceFor(connection);
}
});
}
/**
* Private constructor.
* @param connection
*/
private StableUniqueStanzaIdManager(XMPPConnection connection) {
super(connection);
enable();
}
/**
* Return an instance of the StableUniqueStanzaIdManager for the given connection.
*
* @param connection xmpp-connection
* @return manager instance for the connection
*/
public static StableUniqueStanzaIdManager getInstanceFor(XMPPConnection connection) {
StableUniqueStanzaIdManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new StableUniqueStanzaIdManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
/**
* Start appending origin-id elements to outgoing stanzas and add the feature to disco.
*/
public synchronized void enable() {
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(NAMESPACE);
StanzaFilter filter = new AndFilter(OUTGOING_FILTER, new NotFilter(OUTGOING_FILTER));
connection().addPacketInterceptor(stanzaListener, filter);
}
/**
* Stop appending origin-id elements to outgoing stanzas and remove the feature from disco.
*/
public synchronized void disable() {
ServiceDiscoveryManager.getInstanceFor(connection()).removeFeature(NAMESPACE);
connection().removePacketInterceptor(stanzaListener);
}
/**
* Return true, if we automatically append origin-id elements to outgoing stanzas.
*
* @return true if functionality is enabled, otherwise false.
*/
public synchronized boolean isEnabled() {
ServiceDiscoveryManager disco = ServiceDiscoveryManager.getInstanceFor(connection());
return disco.includesFeature(NAMESPACE);
}
}

View File

@ -0,0 +1,84 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.sid.element;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager;
public class OriginIdElement extends StableAndUniqueIdElement {
public static final String ELEMENT = "origin-id";
public OriginIdElement() {
super();
}
public OriginIdElement(String id) {
super(id);
}
/**
* Add an origin-id element to a message and set the stanzas id to the same id as in the origin-id element.
*
* @param message message.
*/
public static OriginIdElement addOriginId(Message message) {
OriginIdElement originId = new OriginIdElement();
message.addExtension(originId);
// TODO: Find solution to have both the originIds stanzaId and a nice to look at incremental stanzaID.
// message.setStanzaId(originId.getId());
return originId;
}
/**
* Return true, if the message contains a origin-id element.
*
* @param message message
* @return true if the message contains a origin-id, false otherwise.
*/
public static boolean hasOriginId(Message message) {
return getOriginId(message) != null;
}
/**
* Return the origin-id element of a message or null, if absent.
*
* @param message message
* @return origin-id element
*/
public static OriginIdElement getOriginId(Message message) {
return message.getExtension(OriginIdElement.ELEMENT, StableUniqueStanzaIdManager.NAMESPACE);
}
@Override
public String getNamespace() {
return StableUniqueStanzaIdManager.NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public CharSequence toXML() {
return new XmlStringBuilder(this)
.attribute(ATTR_ID, getId())
.closeEmptyElement();
}
}

View File

@ -0,0 +1,44 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.sid.element;
import java.util.UUID;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.util.StringUtils;
public abstract class StableAndUniqueIdElement implements ExtensionElement {
public static final String ATTR_ID = "id";
private final String id;
public StableAndUniqueIdElement() {
this.id = UUID.randomUUID().toString();
}
public String getId() {
return id;
}
public StableAndUniqueIdElement(String id) {
if (StringUtils.isNullOrEmpty(id)) {
throw new IllegalArgumentException("Argument 'id' cannot be null or empty.");
}
this.id = id;
}
}

View File

@ -0,0 +1,81 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.sid.element;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager;
public class StanzaIdElement extends StableAndUniqueIdElement {
public static final String ELEMENT = "stanza-id";
public static final String ATTR_BY = "by";
private final String by;
public StanzaIdElement(String by) {
super();
this.by = by;
}
public StanzaIdElement(String id, String by) {
super(id);
this.by = by;
}
/**
* Return true, if a message contains a stanza-id element.
*
* @param message message
* @return true if message contains stanza-id element, otherwise false.
*/
public static boolean hasStanzaId(Message message) {
return getStanzaId(message) != null;
}
/**
* Return the stanza-id element of a message.
*
* @param message message
* @return stanza-id element of a jid, or null if absent.
*/
public static StanzaIdElement getStanzaId(Message message) {
return message.getExtension(StanzaIdElement.ELEMENT, StableUniqueStanzaIdManager.NAMESPACE);
}
public String getBy() {
return by;
}
@Override
public String getNamespace() {
return StableUniqueStanzaIdManager.NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public XmlStringBuilder toXML() {
return new XmlStringBuilder(this)
.attribute(ATTR_ID, getId())
.attribute(ATTR_BY, getBy())
.closeEmptyElement();
}
}

View File

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0359: Stable and Unique Stanza IDs.
*/
package org.jivesoftware.smackx.sid.element;

View File

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0359: Stable and Unique Stanza IDs.
*/
package org.jivesoftware.smackx.sid;

View File

@ -0,0 +1,32 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.sid.provider;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smackx.sid.element.OriginIdElement;
import org.xmlpull.v1.XmlPullParser;
public class OriginIdProvider extends ExtensionElementProvider<OriginIdElement> {
public static final OriginIdProvider TEST_INSTANCE = new OriginIdProvider();
@Override
public OriginIdElement parse(XmlPullParser parser, int initialDepth) throws Exception {
return new OriginIdElement(parser.getAttributeValue(null, OriginIdElement.ATTR_ID));
}
}

View File

@ -0,0 +1,34 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.sid.provider;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smackx.sid.element.StanzaIdElement;
import org.xmlpull.v1.XmlPullParser;
public class StanzaIdProvider extends ExtensionElementProvider<StanzaIdElement> {
public static StanzaIdProvider TEST_INSTANCE = new StanzaIdProvider();
@Override
public StanzaIdElement parse(XmlPullParser parser, int initialDepth) throws Exception {
String id = parser.getAttributeValue(null, StanzaIdElement.ATTR_ID);
String by = parser.getAttributeValue(null, StanzaIdElement.ATTR_BY);
return new StanzaIdElement(id, by);
}
}

View File

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0359: Stable and Unique Stanza IDs.
*/
package org.jivesoftware.smackx.sid.provider;

View File

@ -0,0 +1,72 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.spoiler;
import java.util.Map;
import java.util.WeakHashMap;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
public final class SpoilerManager extends Manager {
public static final String NAMESPACE_0 = "urn:xmpp:spoiler:0";
private static final Map<XMPPConnection, SpoilerManager> INSTANCES = new WeakHashMap<>();
private final ServiceDiscoveryManager serviceDiscoveryManager;
/**
* Create a new SpoilerManager and add Spoiler to disco features.
*
* @param connection xmpp connection
*/
private SpoilerManager(XMPPConnection connection) {
super(connection);
serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
}
/**
* Begin announcing support for Spoiler messages.
*/
public void startAnnounceSupport() {
serviceDiscoveryManager.addFeature(NAMESPACE_0);
}
/**
* End announcing support for Spoiler messages.
*/
public void stopAnnounceSupport() {
serviceDiscoveryManager.removeFeature(NAMESPACE_0);
}
/**
* Return the connections instance of the SpoilerManager.
*
* @param connection xmpp connection
* @return SpoilerManager
*/
public static SpoilerManager getInstanceFor(XMPPConnection connection) {
SpoilerManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new SpoilerManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}
}

View File

@ -0,0 +1,166 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.spoiler.element;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.spoiler.SpoilerManager;
public class SpoilerElement implements ExtensionElement {
public static final String ELEMENT = "spoiler";
public static final String NAMESPACE = SpoilerManager.NAMESPACE_0;
public static final SpoilerElement EMPTY = new SpoilerElement(null, null);
private final String hint;
private final String language;
/**
* Create a new SpoilerElement with a hint about a content and a language attribute.
*
* @param language language of the hint.
* @param hint hint about the content.
*/
public SpoilerElement(String language, String hint) {
if (StringUtils.isNotEmpty(language) && StringUtils.isNullOrEmpty(hint)) {
throw new IllegalArgumentException("Hint cannot be null or empty if language is not empty.");
}
this.language = language;
this.hint = hint;
}
/**
* Return the hint text of the spoiler.
* May be null.
*
* @return hint text
*/
public String getHint() {
return hint;
}
/**
* Add a SpoilerElement to a message.
*
* @param message message to add the Spoiler to.
*/
public static void addSpoiler(Message message) {
message.addExtension(SpoilerElement.EMPTY);
}
/**
* Add a SpoilerElement with a hint to a message.
*
* @param message Message to add the Spoiler to.
* @param hint Hint about the Spoilers content.
*/
public static void addSpoiler(Message message, String hint) {
message.addExtension(new SpoilerElement(null, hint));
}
/**
* Add a SpoilerElement with a hint in a certain language to a message.
*
* @param message Message to add the Spoiler to.
* @param lang language of the Spoiler hint.
* @param hint hint.
*/
public static void addSpoiler(Message message, String lang, String hint) {
message.addExtension(new SpoilerElement(lang, hint));
}
/**
* Returns true, if the message has at least one spoiler element.
*
* @param message message
* @return true if message has spoiler extension
*/
public static boolean containsSpoiler(Message message) {
return message.hasExtension(SpoilerElement.ELEMENT, NAMESPACE);
}
/**
* Return a map of all spoilers contained in a message.
* The map uses the language of a spoiler as key.
* If a spoiler has no language attribute, its key will be an empty String.
*
* @param message message
* @return map of spoilers
*/
public static Map<String, String> getSpoilers(Message message) {
if (!containsSpoiler(message)) {
return Collections.emptyMap();
}
List<ExtensionElement> spoilers = message.getExtensions(SpoilerElement.ELEMENT, NAMESPACE);
Map<String, String> map = new HashMap<>();
for (ExtensionElement e : spoilers) {
SpoilerElement s = (SpoilerElement) e;
if (s.getLanguage() == null || s.getLanguage().equals("")) {
map.put("", s.getHint());
} else {
map.put(s.getLanguage(), s.getHint());
}
}
return map;
}
/**
* Return the language of the hint.
* May be null.
*
* @return language of hint text
*/
public String getLanguage() {
return language;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public CharSequence toXML() {
XmlStringBuilder xml = new XmlStringBuilder(this);
xml.optXmlLangAttribute(getLanguage());
if (getHint() == null) {
xml.closeEmptyElement();
} else {
xml.rightAngleBracket();
xml.append(getHint());
xml.closeElement(this);
}
return xml;
}
}

View File

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0382: Spoiler Messages.
*/
package org.jivesoftware.smackx.spoiler.element;

View File

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0382: Spoiler Messages.
*/
package org.jivesoftware.smackx.spoiler;

View File

@ -0,0 +1,49 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.spoiler.provider;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
import org.jivesoftware.smack.util.ParserUtils;
import org.jivesoftware.smackx.spoiler.element.SpoilerElement;
import org.xmlpull.v1.XmlPullParser;
public class SpoilerProvider extends ExtensionElementProvider<SpoilerElement> {
public static SpoilerProvider INSTANCE = new SpoilerProvider();
@Override
public SpoilerElement parse(XmlPullParser parser, int initialDepth) throws Exception {
String lang = ParserUtils.getXmlLang(parser);
String hint = null;
outerloop: while (true) {
int tag = parser.next();
switch (tag) {
case TEXT:
hint = parser.getText();
break;
case END_TAG:
break outerloop;
}
}
return new SpoilerElement(lang, hint);
}
}

View File

@ -0,0 +1,20 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.
*/
/**
* Smack's API for XEP-0382: Spoiler Messages.
*/
package org.jivesoftware.smackx.spoiler.provider;

View File

@ -211,6 +211,18 @@
<className>org.jivesoftware.smackx.push_notifications.provider.RemoteDisablingProvider</className>
</extensionProvider>
<!-- XEP-0359: Stable and Unique Stanza IDs -->
<extensionProvider>
<elementName>stanza-id</elementName>
<namespace>urn:xmpp:sid:0</namespace>
<className>org.jivesoftware.smackx.sid.provider.StanzaIdProvider</className>
</extensionProvider>
<extensionProvider>
<elementName>origin-id</elementName>
<namespace>urn:xmpp:sid:0</namespace>
<className>org.jivesoftware.smackx.sid.provider.OriginIdProvider</className>
</extensionProvider>
<!-- XEP-0333: Chat Markers -->
<extensionProvider>
<elementName>markable</elementName>
@ -284,4 +296,18 @@
<className>org.jivesoftware.smackx.eme.provider.ExplicitMessageEncryptionProvider</className>
</extensionProvider>
<!-- XEP-0382: Spoiler Messages -->
<extensionProvider>
<elementName>spoiler</elementName>
<namespace>urn:xmpp:spoiler:0</namespace>
<className>org.jivesoftware.smackx.spoiler.provider.SpoilerProvider</className>
</extensionProvider>
<!-- XEP-0394: Message Markup -->
<extensionProvider>
<elementName>markup</elementName>
<namespace>urn:xmpp:markup:0</namespace>
<className>org.jivesoftware.smackx.message_markup.provider.MarkupElementProvider</className>
</extensionProvider>
</smackProviders>

View File

@ -6,5 +6,6 @@
<className>org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager</className>
<className>org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager</className>
<className>org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager</className>
<className>org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager</className>
</startupClasses>
</smack>

View File

@ -0,0 +1,227 @@
/**
*
* Copyright © 2018 Paul Schaub
*
* 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.message_markup;
import static junit.framework.TestCase.assertEquals;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import java.util.List;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smackx.message_markup.element.BlockQuoteElement;
import org.jivesoftware.smackx.message_markup.element.CodeBlockElement;
import org.jivesoftware.smackx.message_markup.element.ListElement;
import org.jivesoftware.smackx.message_markup.element.MarkupElement;
import org.jivesoftware.smackx.message_markup.element.SpanElement;
import org.jivesoftware.smackx.message_markup.provider.MarkupElementProvider;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
public class MessageMarkupTest extends SmackTestSuite {
@Test
public void emphasisTest() throws Exception {
String xml =
"<markup xmlns='urn:xmpp:markup:0'>" +
"<span start='9' end='15'>" +
"<emphasis/>" +
"</span>" +
"</markup>";
MarkupElement.Builder m = MarkupElement.getBuilder();
m.setEmphasis(9, 15);
assertXMLEqual(xml, m.build().toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
MarkupElement parsed = new MarkupElementProvider().parse(parser);
List<MarkupElement.MarkupChildElement> children = parsed.getChildElements();
assertEquals(1, children.size());
SpanElement spanElement = (SpanElement) children.get(0);
assertEquals(9, spanElement.getStart());
assertEquals(15, spanElement.getEnd());
assertEquals(1, spanElement.getStyles().size());
assertEquals(SpanElement.SpanStyle.emphasis, spanElement.getStyles().iterator().next());
}
@Test
public void codeTest() throws Exception {
String xml =
"<markup xmlns='urn:xmpp:markup:0'>" +
"<span start='9' end='15'>" +
"<code/>" +
"</span>" +
"</markup>";
MarkupElement.Builder m = MarkupElement.getBuilder();
m.setCode(9, 15);
assertXMLEqual(xml, m.build().toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
MarkupElement parsed = new MarkupElementProvider().parse(parser);
List<MarkupElement.MarkupChildElement> children = parsed.getChildElements();
assertEquals(1, children.size());
SpanElement spanElement = (SpanElement) children.get(0);
assertEquals(9, spanElement.getStart());
assertEquals(15, spanElement.getEnd());
assertEquals(1, spanElement.getStyles().size());
assertEquals(SpanElement.SpanStyle.code, spanElement.getStyles().iterator().next());
}
@Test
public void deletedTest() throws Exception {
String xml =
"<markup xmlns='urn:xmpp:markup:0'>" +
"<span start='9' end='15'>" +
"<deleted/>" +
"</span>" +
"</markup>";
MarkupElement.Builder m = MarkupElement.getBuilder();
m.setDeleted(9, 15);
assertXMLEqual(xml, m.build().toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
MarkupElement parsed = new MarkupElementProvider().parse(parser);
List<MarkupElement.MarkupChildElement> children = parsed.getChildElements();
assertEquals(1, children.size());
SpanElement spanElement = (SpanElement) children.get(0);
assertEquals(9, spanElement.getStart());
assertEquals(15, spanElement.getEnd());
assertEquals(1, spanElement.getStyles().size());
assertEquals(SpanElement.SpanStyle.deleted, spanElement.getStyles().iterator().next());
}
@Test(expected = IllegalArgumentException.class)
public void wrongStartEndTest() {
MarkupElement.getBuilder().setEmphasis(12, 10);
}
@Test(expected = IllegalArgumentException.class)
public void overlappingSpansTest() {
MarkupElement.Builder m = MarkupElement.getBuilder();
m.setEmphasis(0, 10);
m.setDeleted(5, 15);
}
@Test
public void codeBlockTest() throws Exception {
String xml =
"<markup xmlns='urn:xmpp:markup:0'>" +
"<bcode start='23' end='48'/>" +
"</markup>";
MarkupElement.Builder m = MarkupElement.getBuilder();
m.setCodeBlock(23, 48);
assertXMLEqual(xml, m.build().toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
MarkupElement parsed = new MarkupElementProvider().parse(parser);
List<MarkupElement.MarkupChildElement> children = parsed.getChildElements();
assertEquals(1, children.size());
CodeBlockElement codeBlock = (CodeBlockElement) children.get(0);
assertEquals(23, codeBlock.getStart());
assertEquals(48, codeBlock.getEnd());
}
@Test
public void listTest() throws Exception {
String xml =
"<markup xmlns='urn:xmpp:markup:0'>" +
"<list start='31' end='89'>" +
"<li start='31'/>" +
"<li start='47'/>" +
"<li start='61'/>" +
"<li start='69'/>" +
"</list>" +
"</markup>";
MarkupElement.Builder m = MarkupElement.getBuilder();
m = m.beginList()
.addEntry(31, 47)
.addEntry(47, 61)
.addEntry(61, 69)
.addEntry(69, 89)
.endList();
assertXMLEqual(xml, m.build().toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
MarkupElement parsed = new MarkupElementProvider().parse(parser);
List<MarkupElement.MarkupChildElement> children = parsed.getChildElements();
assertEquals(1, children.size());
ListElement list = (ListElement) children.get(0);
assertEquals(31, list.getStart());
assertEquals(89, list.getEnd());
assertEquals(4, list.getEntries().size());
assertEquals(list.getStart(), list.getEntries().get(0).getStart());
assertEquals(47, list.getEntries().get(1).getStart());
}
@Test(expected = IllegalArgumentException.class)
public void listWrongSecondEntryTest() {
MarkupElement.Builder m = MarkupElement.getBuilder();
m.beginList().addEntry(0,1).addEntry(3,4);
}
@Test
public void blockQuoteTest() throws Exception {
String xml =
"<markup xmlns='urn:xmpp:markup:0'>" +
"<bquote start='9' end='32'/>" +
"</markup>";
MarkupElement.Builder m = MarkupElement.getBuilder();
m.setBlockQuote(9 ,32);
assertXMLEqual(xml, m.build().toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
MarkupElement parsed = new MarkupElementProvider().parse(parser);
List<MarkupElement.MarkupChildElement> children = parsed.getChildElements();
assertEquals(1, children.size());
BlockQuoteElement quote = (BlockQuoteElement) children.get(0);
assertEquals(9, quote.getStart());
assertEquals(32, quote.getEnd());
}
@Test
public void nestedBlockQuoteTest() throws Exception {
String xml =
"<markup xmlns='urn:xmpp:markup:0'>" +
"<bquote start='0' end='57'/>" +
"<bquote start='11' end='34'/>" +
"</markup>";
MarkupElement.Builder m = MarkupElement.getBuilder();
m.setBlockQuote(0, 57);
m.setBlockQuote(11, 34);
assertXMLEqual(xml, m.build().toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
MarkupElement parsed = new MarkupElementProvider().parse(parser);
List<MarkupElement.MarkupChildElement> children = parsed.getChildElements();
assertEquals(2, children.size());
BlockQuoteElement q1 = (BlockQuoteElement) children.get(0);
BlockQuoteElement q2 = (BlockQuoteElement) children.get(1);
assertEquals(0, q1.getStart());
assertEquals(57, q1.getEnd());
assertEquals(11, q2.getStart());
assertEquals(34, q2.getEnd());
}
}

View File

@ -0,0 +1,84 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.sid;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smackx.sid.element.OriginIdElement;
import org.jivesoftware.smackx.sid.element.StanzaIdElement;
import org.jivesoftware.smackx.sid.provider.OriginIdProvider;
import org.jivesoftware.smackx.sid.provider.StanzaIdProvider;
import org.junit.Test;
public class StableUniqueStanzaIdTest extends SmackTestSuite {
@Test
public void stanzaIdProviderTest() throws Exception {
String xml = "<stanza-id xmlns='urn:xmpp:sid:0' id='de305d54-75b4-431b-adb2-eb6b9e546013' by='alice@wonderland.lit' />";
StanzaIdElement element = new StanzaIdElement("de305d54-75b4-431b-adb2-eb6b9e546013", "alice@wonderland.lit");
assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId());
assertEquals("alice@wonderland.lit", element.getBy());
assertXMLEqual(xml, element.toXML().toString());
StanzaIdElement parsed = StanzaIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml));
assertEquals(element.getId(), parsed.getId());
assertEquals(element.getBy(), parsed.getBy());
}
@Test
public void originIdProviderTest() throws Exception {
String xml = "<origin-id xmlns='urn:xmpp:sid:0' id='de305d54-75b4-431b-adb2-eb6b9e546013' />";
OriginIdElement element = new OriginIdElement("de305d54-75b4-431b-adb2-eb6b9e546013");
assertEquals("de305d54-75b4-431b-adb2-eb6b9e546013", element.getId());
assertXMLEqual(xml, element.toXML().toString());
OriginIdElement parsed = OriginIdProvider.TEST_INSTANCE.parse(TestUtils.getParser(xml));
assertEquals(element.getId(), parsed.getId());
}
@Test
public void createOriginIdTest() {
OriginIdElement element = new OriginIdElement();
assertNotNull(element);
assertEquals(StableUniqueStanzaIdManager.NAMESPACE, element.getNamespace());
assertEquals(36, element.getId().length());
}
@Test
public void fromMessageTest() {
Message message = new Message();
assertFalse(OriginIdElement.hasOriginId(message));
assertFalse(StanzaIdElement.hasStanzaId(message));
OriginIdElement.addOriginId(message);
assertTrue(OriginIdElement.hasOriginId(message));
StanzaIdElement stanzaId = new StanzaIdElement("alice@wonderland.lit");
message.addExtension(stanzaId);
assertTrue(StanzaIdElement.hasStanzaId(message));
assertEquals(stanzaId, StanzaIdElement.getStanzaId(message));
}
}

View File

@ -0,0 +1,128 @@
/**
*
* Copyright 2018 Paul Schaub
*
* 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.spoiler;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
import java.util.Map;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smackx.spoiler.element.SpoilerElement;
import org.jivesoftware.smackx.spoiler.provider.SpoilerProvider;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
public class SpoilerTest extends SmackTestSuite {
@Test
public void emptySpoilerTest() throws Exception {
final String xml = "<spoiler xmlns='urn:xmpp:spoiler:0'/>";
Message message = new Message();
SpoilerElement.addSpoiler(message);
SpoilerElement empty = message.getExtension(SpoilerElement.ELEMENT, SpoilerManager.NAMESPACE_0);
assertNull(empty.getHint());
assertNull(empty.getLanguage());
assertXMLEqual(xml, empty.toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
SpoilerElement parsed = SpoilerProvider.INSTANCE.parse(parser);
assertXMLEqual(xml, parsed.toXML().toString());
}
@Test
public void hintSpoilerTest() throws Exception {
final String xml = "<spoiler xmlns='urn:xmpp:spoiler:0'>Love story end</spoiler>";
Message message = new Message();
SpoilerElement.addSpoiler(message, "Love story end");
SpoilerElement withHint = message.getExtension(SpoilerElement.ELEMENT, SpoilerManager.NAMESPACE_0);
assertEquals("Love story end", withHint.getHint());
assertNull(withHint.getLanguage());
assertXMLEqual(xml, withHint.toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
SpoilerElement parsed = SpoilerProvider.INSTANCE.parse(parser);
assertXMLEqual(xml, parsed.toXML().toString());
}
@Test
public void i18nHintSpoilerTest() throws Exception {
final String xml = "<spoiler xml:lang='de' xmlns='urn:xmpp:spoiler:0'>Der Kuchen ist eine Lüge!</spoiler>";
Message message = new Message();
SpoilerElement.addSpoiler(message, "de", "Der Kuchen ist eine Lüge!");
SpoilerElement i18nHint = message.getExtension(SpoilerElement.ELEMENT, SpoilerManager.NAMESPACE_0);
assertEquals("Der Kuchen ist eine Lüge!", i18nHint.getHint());
assertEquals("de", i18nHint.getLanguage());
assertXMLEqual(xml, i18nHint.toXML().toString());
XmlPullParser parser = TestUtils.getParser(xml);
SpoilerElement parsed = SpoilerProvider.INSTANCE.parse(parser);
assertEquals(i18nHint.getLanguage(), parsed.getLanguage());
assertXMLEqual(xml, parsed.toXML().toString());
}
@Test
public void getSpoilersTest() {
Message m = new Message();
assertTrue(SpoilerElement.getSpoilers(m).isEmpty());
SpoilerElement.addSpoiler(m);
assertTrue(SpoilerElement.containsSpoiler(m));
Map<String, String> spoilers = SpoilerElement.getSpoilers(m);
assertEquals(1, spoilers.size());
assertEquals(null, spoilers.get(""));
final String spoilerText = "Spoiler Text";
SpoilerElement.addSpoiler(m, "de", spoilerText);
spoilers = SpoilerElement.getSpoilers(m);
assertEquals(2, spoilers.size());
assertEquals(spoilerText, spoilers.get("de"));
}
@Test(expected = IllegalArgumentException.class)
public void spoilerCheckArgumentsNullTest() {
@SuppressWarnings("unused")
SpoilerElement spoilerElement = new SpoilerElement("de", null);
}
@Test(expected = IllegalArgumentException.class)
public void spoilerCheckArgumentsEmptyTest() {
@SuppressWarnings("unused")
SpoilerElement spoilerElement = new SpoilerElement("de", "");
}
}

View File

@ -271,7 +271,11 @@ public final class AdHocCommandManager extends Manager {
* @throws XMPPException if the operation failed for some reason.
* @throws SmackException if there was no response from the server.
* @throws InterruptedException
* @deprecated This method uses no longer existent XEP-0030 features and will be removed.
*/
@SuppressWarnings("deprecation")
@Deprecated
// TODO: Remove in Smack 4.4.
public void publishCommands(Jid jid) throws XMPPException, SmackException, InterruptedException {
// Collects the commands to publish as items
DiscoverItems discoverItems = new DiscoverItems();

View File

@ -27,7 +27,6 @@ import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
@ -71,8 +70,6 @@ import org.jxmpp.util.cache.ExpirationCache;
*/
public final class ServiceDiscoveryManager extends Manager {
private static final Logger LOGGER = Logger.getLogger(ServiceDiscoveryManager.class.getName());
private static final String DEFAULT_IDENTITY_NAME = "Smack";
private static final String DEFAULT_IDENTITY_CATEGORY = "client";
private static final String DEFAULT_IDENTITY_TYPE = "pc";
@ -595,7 +592,10 @@ public final class ServiceDiscoveryManager extends Manager {
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
* @deprecated The disco-publish feature was removed from XEP-0030 in 2008 in favor of XEP-0060: Publish-Subscribe.
*/
@Deprecated
// TODO: Remove in Smack 4.4
public boolean canPublishItems(Jid entityID) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
DiscoverInfo info = discoverInfo(entityID);
return canPublishItems(info);
@ -609,7 +609,10 @@ public final class ServiceDiscoveryManager extends Manager {
*
* @param info the discover info stanza(/packet) to check.
* @return true if the server supports publishing of items.
* @deprecated The disco-publish feature was removed from XEP-0030 in 2008 in favor of XEP-0060: Publish-Subscribe.
*/
@Deprecated
// TODO: Remove in Smack 4.4
public static boolean canPublishItems(DiscoverInfo info) {
return info.containsFeature("http://jabber.org/protocol/disco#publish");
}
@ -626,7 +629,10 @@ public final class ServiceDiscoveryManager extends Manager {
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
* @deprecated The disco-publish feature was removed from XEP-0030 in 2008 in favor of XEP-0060: Publish-Subscribe.
*/
@Deprecated
// TODO: Remove in Smack 4.4
public void publishItems(Jid entityID, DiscoverItems discoverItems) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
publishItems(entityID, null, discoverItems);
}
@ -644,7 +650,10 @@ public final class ServiceDiscoveryManager extends Manager {
* @throws NoResponseException if there was no response from the server.
* @throws NotConnectedException
* @throws InterruptedException
* @deprecated The disco-publish feature was removed from XEP-0030 in 2008 in favor of XEP-0060: Publish-Subscribe.
*/
@Deprecated
// TODO: Remove in Smack 4.4
public void publishItems(Jid entityID, String node, DiscoverItems discoverItems) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException
{
discoverItems.setType(IQ.Type.set);