mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-12-26 04:28:00 +01:00
c9a9982cef
While markdown is easier to write, Smack's markdown documentation was never tightly coupled with the source. For example, the markdown documentation never provided links to the actual Java classes and methods. This poses the risk that the documentation and the code diverge over time. Furthermore, javadoc is constantly improving (for example @snippet annotations) and I expect that one will be able to write javadoc in markdown. Fixes SMACK-928.
163 lines
5.1 KiB
Markdown
163 lines
5.1 KiB
Markdown
# Building Smack
|
|
|
|
## Linux
|
|
|
|
Building Smack is as simple as
|
|
|
|
```
|
|
git clone git@github.com:igniterealtime/Smack.git
|
|
cd Smack
|
|
gradle assemble
|
|
```
|
|
|
|
## Mac
|
|
|
|
Smack requires a case-sensitive file system in order to build. Unfortunately, the macOS operating system is case-insensitive by default.
|
|
To get around this, you can create a case-sensitive disk image to work from.
|
|
|
|
1. Launch Disk Utility (Applications > Utilities)
|
|
2. Click the +, or go to Edit > Add APFS Volume
|
|
3. Give it a name, e.g. "Smack"
|
|
4. Change the format to "APFS (Case-sensitive)"
|
|
5. Click Add
|
|
|
|
It'll auto-mount into /Volumes, e.g. /Volumes/Smack
|
|
|
|
```bash
|
|
cd /Volumes/Smack
|
|
git clone git@github.com:igniterealtime/Smack.git
|
|
cd Smack
|
|
gradle assemble
|
|
```
|
|
|
|
## Windows
|
|
|
|
Smack requires a case-sensitive file system in order to build. Unfortunately, Windows NTFS is case-insensitive by default.
|
|
To get around this, you can set specific folders as case-sensitive (requires Windows 10 v1803 or higher).
|
|
|
|
In an Administrator console:
|
|
|
|
```batch
|
|
fsutil.exe file SetCaseSensitiveInfo C:\git\Smack enable
|
|
cd \git\Smack
|
|
git clone git@github.com:igniterealtime/Smack.git
|
|
cd Smack
|
|
gradle assemble
|
|
```
|
|
|
|
# IDE Config
|
|
|
|
### Eclipse
|
|
|
|
Import IDE settings from `./resources/eclipse/` to configure proper ordering of imports and correct formatting that should pass the CheckStyle rules.
|
|
|
|
### IntelliJ IDEA
|
|
|
|
Import Java Code Style settings from `./resources/intellij/smack_formatter.xml` to configure import optimisation and code formatting to pass the CheckStyle rules when building or submitting PRs.
|
|
|
|
_We've noticed, at time of writing, that IntelliJ often requires a restart when applying new rules - no amount of OK/Apply will do the trick._
|
|
|
|
# Smack Providers
|
|
|
|
Providers are responsible for parsing the XMPP XML stream into new Java objects.
|
|
|
|
## Provider Design
|
|
|
|
Assume you want to parse the following stanza extension element
|
|
|
|
```xml
|
|
<myExtension attrFoo='fourthyTwo'>
|
|
<myElement>Foo is greater then Bar</myElement>
|
|
<myInfo alpha='true' delta='-1337'/>
|
|
</myExtension>
|
|
```
|
|
|
|
then the related provider would look like this
|
|
|
|
```java
|
|
public MyExtension parse(XmlPullParser parser, int initialDepth) {
|
|
MyElement myElement = null;
|
|
MyInfo myInfo = null;
|
|
String attrFoo = parser.getAttributeValue("", "attrFoo");
|
|
|
|
// Main parsing loop, use a loop label instead of "boolean done"
|
|
outerloop: while(true) {
|
|
// Make sure to have already parse all attributes of the outermost element,
|
|
// i.e. 'attrFoo' of 'myExtension' in this example. Then advance the parser
|
|
XmlPullParser.Event event = parser.next();
|
|
|
|
// Use switch/case of int instead of a if/else-if cascade
|
|
switch (event) {
|
|
case START_ELEMENT:
|
|
// Determine the name of the element which start tag we are seeing
|
|
String name = parser.getName();
|
|
// We can use switch/case of Strings since Java7, make use of its advantages
|
|
// and collect the values of the sub elements. If the sub elements are more
|
|
// complex then those of this example, consider creating extra *private static*
|
|
// parsing methods for them.
|
|
switch(name) {
|
|
case "myElement":
|
|
// You should only use XmlPullParser.nextText() when the element is
|
|
// required to have a text.
|
|
myElement = new MyElement(parser.nextText());
|
|
break;
|
|
case "myInfo";
|
|
// Use ParserUtils to parse Java primitives
|
|
boolenan alpha = ParserUtils.getBooleanAttribute(parser, "alpha");
|
|
int delta = ParserUtils.getIntegerAttribute(parser, "delta");
|
|
myInfo = new MyInfo(alpha, delta);
|
|
break;
|
|
}
|
|
break;
|
|
case END_ELEMENT:
|
|
// The abort condition with the break labeled loop statement
|
|
if (parser.getDepth() == initialDepth) {
|
|
break outerloop;
|
|
}
|
|
break;
|
|
default:
|
|
// Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Create the actual class at the very end, design the classes as immutable as possible
|
|
return new MyExtension(attrFoo, myElement, myInfo);
|
|
}
|
|
```
|
|
|
|
## Common Pitfalls
|
|
|
|
Use a `long` instead of `int` when the XML schema says `xs:unsignedInt`, because Java's `int` range is to small for this XML numeric data type.
|
|
|
|
# Stanzas
|
|
## General Rules
|
|
|
|
All classes which subclass `TopLevelStreamElement` and `ExtensionElement` must either
|
|
|
|
1. be immutable (and ideally provide a Builder)
|
|
2. implement `TypedCloneable`
|
|
|
|
and must be `Serializable`.
|
|
The reason that it must be either 1. or 2. is that it makes no sense to clone an inmutable instance.
|
|
The preferred option is 1.
|
|
|
|
Note that there is legacy code in Smack which does not follow these rules. Patches are welcome.
|
|
|
|
## ExtensionElement
|
|
|
|
Extension elements are XML elements that are used in various parts and levels of stanzas.
|
|
|
|
## The static `from(Stanza)` Method
|
|
|
|
Every ExtensionElement class must have a static `from()` method that retrieves that extension for a given Stanza (if any).
|
|
|
|
Sample Code
|
|
|
|
```java
|
|
public static RSMSet from(Stanza) {
|
|
return packet.getExtension(ELEMENT, NAMESPACE);
|
|
}
|
|
```
|
|
|
|
Sometimes certain ExtensionElement's are only found in one stanza type, in that case, specify the parameter type. For example `public static CarbonExtension getFrom(Message)`.
|