-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from olivergondza/namespaces
Add support for XML namespaces
- Loading branch information
Showing
11 changed files
with
782 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 85 additions & 35 deletions
120
src/main/java/com/github/olivergondza/saxeed/Subscribed.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,107 @@ | ||
package com.github.olivergondza.saxeed; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
/** | ||
* Criteria for Visitor-to-tags subscription. | ||
*/ | ||
public abstract class Subscribed { | ||
@FunctionalInterface | ||
public interface Subscribed { | ||
|
||
/** | ||
* Subscribe to all tags in the document. | ||
*/ | ||
static Subscribed toAll() { | ||
return Builder.ALL; | ||
} | ||
|
||
/** | ||
* Build subscription criteria. | ||
*/ | ||
static Subscribed.Builder to() { | ||
return new Subscribed.Builder(); | ||
} | ||
|
||
boolean isSubscribed(TagName tagName); | ||
|
||
private static final Subscribed ALL = new Subscribed() { | ||
@Override | ||
public boolean isSubscribed(String tagName) { | ||
return true; | ||
final class Builder { | ||
private static final Subscribed ALL = tagName -> true; | ||
|
||
private Subscribed nsFilter = ALL; | ||
private Subscribed tagFilter = ALL; | ||
|
||
Builder() { | ||
} | ||
}; | ||
|
||
private static final Subscribed NONE = new Subscribed() { | ||
@Override | ||
public boolean isSubscribed(String tagName) { | ||
return false; | ||
/** | ||
* Match tags regardless of namespace status. | ||
*/ | ||
public Builder anyNamespace() { | ||
nsFilter = ALL; | ||
return this; | ||
} | ||
}; | ||
|
||
public static Subscribed toAll() { | ||
return ALL; | ||
} | ||
/** | ||
* Match tags in default, not overridden namespace. | ||
*/ | ||
public Builder noNamespace() { | ||
nsFilter = name -> name.getNsUri().isEmpty(); | ||
return this; | ||
} | ||
|
||
public static Subscribed to(String... tagNames) { | ||
return tagNames.length == 0 | ||
? NONE | ||
: new Tags(tagNames) | ||
; | ||
} | ||
/** | ||
* Match tags in default namespace, named or not. | ||
*/ | ||
public Builder defaultNamespace() { | ||
nsFilter = name -> name.getNsPrefix().isEmpty(); | ||
return this; | ||
} | ||
|
||
public abstract boolean isSubscribed(String tagName); | ||
/** | ||
* Match tags in namespace its uri is in the arguments. | ||
*/ | ||
public Builder namespaceUris(String... uris) { | ||
List<String> namespaces = list("namespace", uris); | ||
nsFilter = name -> namespaces.contains(name.getNsUri()); | ||
return this; | ||
} | ||
|
||
private static final class Tags extends Subscribed { | ||
/** | ||
* Match any local tag name. | ||
*/ | ||
public Builder anyTag() { | ||
tagFilter = ALL; | ||
return this; | ||
} | ||
|
||
private final String[] tagNames; | ||
/** | ||
* Match local tag names specified in arguments. | ||
*/ | ||
public Builder tagNames(String... locals) { | ||
List<String> tags = list("tag", locals); | ||
tagFilter = name -> tags.contains(name.getLocal()); | ||
return this; | ||
} | ||
|
||
public Tags(String... tagNames) { | ||
this.tagNames = tagNames; | ||
for (String name: tagNames) { | ||
if (name == null || name.isEmpty()) { | ||
throw new IllegalArgumentException("Empty tag name specified for subscription"); | ||
} | ||
} | ||
public Subscribed build() { | ||
assert nsFilter != null; | ||
assert tagFilter != null; | ||
|
||
return name -> tagFilter.isSubscribed(name) && nsFilter.isSubscribed(name); | ||
} | ||
|
||
@Override | ||
public boolean isSubscribed(String tagName) { | ||
for (String name: tagNames) { | ||
if (name.equals(tagName)) return true; | ||
private static List<String> list(String type, String[] vals) { | ||
if (vals.length == 0) { | ||
throw new IllegalArgumentException("Subscribing to 0 " + type + "s means no subscription at all"); | ||
} | ||
|
||
return false; | ||
for (String name: vals) { | ||
if (name == null || name.isEmpty()) { | ||
throw new IllegalArgumentException("Empty " + type + " name specified for subscription in: " + Arrays.toString(vals)); | ||
} | ||
} | ||
return List.of(vals); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package com.github.olivergondza.saxeed; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* Namespace aware tag name. | ||
*/ | ||
public class TagName { | ||
|
||
private final String local; | ||
private final String qName; | ||
|
||
private final String uri; | ||
private final String prefix; | ||
|
||
public static TagName fromSaxArgs(String uri, String localName, String qName) { | ||
boolean noNsInTagName = Objects.equals(localName, qName); | ||
if (uri.isEmpty()) { | ||
assert noNsInTagName; | ||
return new TagName("", "", localName); | ||
} | ||
|
||
assert qName.endsWith(localName): String.format("Tag name ('%s') does not start with local name ('%s')", qName, localName); | ||
|
||
if (noNsInTagName) { | ||
return new TagName(uri, "", localName); | ||
} | ||
|
||
return new TagName(uri, qName.replaceFirst(":.*", ""), localName); | ||
} | ||
|
||
public static TagName noNs(String local) { | ||
return new TagName("", "", local); | ||
} | ||
|
||
public static TagName withNs(String uri, String local) { | ||
return new TagName(uri, "", local); | ||
} | ||
|
||
public static TagName withNs(String uri, String prefix, String local) { | ||
return new TagName(uri, prefix, local); | ||
} | ||
|
||
/** | ||
* Create tag in specified namespace. | ||
*/ | ||
public TagName(String uri, String prefix, String local) { | ||
this.uri = Objects.requireNonNull(uri); | ||
this.prefix = Objects.requireNonNull(prefix); | ||
this.local = Objects.requireNonNull(local); | ||
|
||
if (local.isEmpty()) throw new IllegalArgumentException("Tag cannot have local name an empty string"); | ||
|
||
if (uri.isEmpty() && !prefix.isEmpty()) throw new IllegalArgumentException("Tag cannot have NS name, but no NS URI"); | ||
|
||
qName = prefix.isEmpty() | ||
? local | ||
: prefix + ":" + local | ||
; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.format("TagName{local='%s', uri='%s', ns='%s'}", local, uri, prefix); | ||
} | ||
|
||
public String getNsUri() { | ||
return uri; | ||
} | ||
|
||
public String getNsPrefix() { | ||
return prefix; | ||
} | ||
|
||
public String getLocal() { | ||
return local; | ||
} | ||
|
||
public String getQualifiedName() { | ||
return qName; | ||
} | ||
|
||
public TagName inheritNamespace(String name) { | ||
return new TagName(uri, prefix, name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.