Add attributes to start tags
This commit is contained in:
parent
bf8ff03b1c
commit
be484765da
@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @see Inline
|
||||
@ -32,6 +33,9 @@ public interface HtmlTag {
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
@NonNull
|
||||
Map<String, String> attributes();
|
||||
|
||||
/**
|
||||
* Represents <em>really</em> inline HTML tags (unline commonmark definitions)
|
||||
*/
|
||||
|
@ -5,6 +5,7 @@ import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
abstract class HtmlTagImpl implements HtmlTag {
|
||||
|
||||
@ -12,11 +13,13 @@ abstract class HtmlTagImpl implements HtmlTag {
|
||||
|
||||
final String name;
|
||||
final int start;
|
||||
final Map<String, String> attributes;
|
||||
int end = NO_VALUE;
|
||||
|
||||
protected HtmlTagImpl(@NonNull String name, int start) {
|
||||
protected HtmlTagImpl(@NonNull String name, int start, @NonNull Map<String, String> attributes) {
|
||||
this.name = name;
|
||||
this.start = start;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -40,16 +43,23 @@ abstract class HtmlTagImpl implements HtmlTag {
|
||||
return start == end;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Map<String, String> attributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
boolean isClosed() {
|
||||
return end > NO_VALUE;
|
||||
}
|
||||
|
||||
abstract void closeAt(int end);
|
||||
|
||||
|
||||
static class InlineImpl extends HtmlTagImpl implements Inline {
|
||||
|
||||
InlineImpl(@NonNull String name, int start) {
|
||||
super(name, start);
|
||||
InlineImpl(@NonNull String name, int start, @NonNull Map<String, String> attributes) {
|
||||
super(name, start, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -65,6 +75,7 @@ abstract class HtmlTagImpl implements HtmlTag {
|
||||
"name='" + name + '\'' +
|
||||
", start=" + start +
|
||||
", end=" + end +
|
||||
", attributes=" + attributes +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -74,20 +85,28 @@ abstract class HtmlTagImpl implements HtmlTag {
|
||||
@NonNull
|
||||
static BlockImpl root() {
|
||||
//noinspection ConstantConditions
|
||||
return new BlockImpl("", 0, null);
|
||||
return new BlockImpl("", 0, null, null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static BlockImpl create(@NonNull String name, int start, @NonNull BlockImpl parent) {
|
||||
return new BlockImpl(name, start, parent);
|
||||
static BlockImpl create(
|
||||
@NonNull String name,
|
||||
int start,
|
||||
@NonNull Map<String, String> attributes,
|
||||
@NonNull BlockImpl parent) {
|
||||
return new BlockImpl(name, start, attributes, parent);
|
||||
}
|
||||
|
||||
final BlockImpl parent;
|
||||
List<BlockImpl> children;
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
BlockImpl(@NonNull String name, int start, @NonNull BlockImpl parent) {
|
||||
super(name, start);
|
||||
BlockImpl(
|
||||
@NonNull String name,
|
||||
int start,
|
||||
@NonNull Map<String, String> attributes,
|
||||
@NonNull BlockImpl parent) {
|
||||
super(name, start, attributes);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@ -127,12 +146,24 @@ abstract class HtmlTagImpl implements HtmlTag {
|
||||
return (List<Block>) (List<? extends Block>) children;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Map<String, String> attributes() {
|
||||
//noinspection ConstantConditions
|
||||
if (attributes == null) {
|
||||
throw new IllegalStateException("#attributes() getter was called on the root node " +
|
||||
"which should not be exposed outside internal usage");
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BlockImpl{" +
|
||||
"name='" + name + '\'' +
|
||||
", start=" + start +
|
||||
", end=" + end +
|
||||
", attributes=" + attributes +
|
||||
", parent=" + (parent != null ? parent.name : null) +
|
||||
", children=" + children +
|
||||
'}';
|
||||
|
@ -7,14 +7,19 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import ru.noties.markwon.html.HtmlTag.Block;
|
||||
import ru.noties.markwon.html.HtmlTag.Inline;
|
||||
import ru.noties.markwon.html.HtmlTagImpl.BlockImpl;
|
||||
import ru.noties.markwon.html.HtmlTagImpl.InlineImpl;
|
||||
import ru.noties.markwon.html.jsoup.nodes.Attribute;
|
||||
import ru.noties.markwon.html.jsoup.nodes.Attributes;
|
||||
import ru.noties.markwon.html.jsoup.parser.CharacterReader;
|
||||
import ru.noties.markwon.html.jsoup.parser.ParseErrorList;
|
||||
import ru.noties.markwon.html.jsoup.parser.Token;
|
||||
@ -209,7 +214,7 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
||||
|
||||
final String name = startTag.normalName;
|
||||
|
||||
final InlineImpl inline = new InlineImpl(name, output.length());
|
||||
final InlineImpl inline = new InlineImpl(name, output.length(), extractAttributes(startTag));
|
||||
|
||||
if (isVoidTag(name)
|
||||
|| startTag.selfClosing) {
|
||||
@ -271,7 +276,7 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
||||
|
||||
final int start = output.length();
|
||||
|
||||
final BlockImpl block = BlockImpl.create(name, start, currentBlock);
|
||||
final BlockImpl block = BlockImpl.create(name, start, extractAttributes(startTag), currentBlock);
|
||||
|
||||
final boolean isVoid = isVoidTag(name) || startTag.selfClosing;
|
||||
if (isVoid) {
|
||||
@ -396,4 +401,25 @@ public class MarkwonHtmlParserImpl extends MarkwonHtmlParser {
|
||||
append(output, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected static Map<String, String> extractAttributes(@NonNull Token.StartTag startTag) {
|
||||
|
||||
Map<String, String> map;
|
||||
|
||||
final Attributes attributes = startTag.attributes;
|
||||
final int size = attributes.size();
|
||||
|
||||
if (size > 0) {
|
||||
map = new HashMap<>(size);
|
||||
for (Attribute attribute : attributes) {
|
||||
map.put(attribute.getKey().toLowerCase(Locale.US), attribute.getValue());
|
||||
}
|
||||
map = Collections.unmodifiableMap(map);
|
||||
} else {
|
||||
map = Collections.emptyMap();
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
@ -137,13 +137,13 @@ public class Attribute implements Map.Entry<String, String>, Cloneable {
|
||||
// return new Attribute(unencodedKey, value, null); // parent will get set when Put
|
||||
// }
|
||||
|
||||
protected boolean isDataAttribute() {
|
||||
return isDataAttribute(key);
|
||||
}
|
||||
|
||||
protected static boolean isDataAttribute(String key) {
|
||||
return key.startsWith(Attributes.dataPrefix) && key.length() > Attributes.dataPrefix.length();
|
||||
}
|
||||
// protected boolean isDataAttribute() {
|
||||
// return isDataAttribute(key);
|
||||
// }
|
||||
//
|
||||
// protected static boolean isDataAttribute(String key) {
|
||||
// return key.startsWith(Attributes.dataPrefix) && key.length() > Attributes.dataPrefix.length();
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Collapsible if it's a boolean attribute and value is empty or same as name
|
||||
|
@ -28,7 +28,7 @@ import static ru.noties.markwon.html.jsoup.helper.Normalizer.lowerCase;
|
||||
* @author Jonathan Hedley, jonathan@hedley.net
|
||||
*/
|
||||
public class Attributes implements Iterable<Attribute>, Cloneable {
|
||||
protected static final String dataPrefix = "data-";
|
||||
// protected static final String dataPrefix = "data-";
|
||||
private static final int InitialCapacity = 4; // todo - analyze Alexa 1MM sites, determine best setting
|
||||
|
||||
// manages the key/val arrays
|
||||
@ -282,14 +282,14 @@ public class Attributes implements Iterable<Attribute>, Cloneable {
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a filtered view of attributes that are HTML5 custom data attributes; that is, attributes with keys
|
||||
* starting with {@code data-}.
|
||||
* @return map of custom data attributes.
|
||||
*/
|
||||
public Map<String, String> dataset() {
|
||||
return new Dataset(this);
|
||||
}
|
||||
// /**
|
||||
// * Retrieves a filtered view of attributes that are HTML5 custom data attributes; that is, attributes with keys
|
||||
// * starting with {@code data-}.
|
||||
// * @return map of custom data attributes.
|
||||
// */
|
||||
// public Map<String, String> dataset() {
|
||||
// return new Dataset(this);
|
||||
// }
|
||||
|
||||
// /**
|
||||
// Get the HTML representation of these attributes.
|
||||
@ -380,65 +380,65 @@ public class Attributes implements Iterable<Attribute>, Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
private static class Dataset extends AbstractMap<String, String> {
|
||||
private final Attributes attributes;
|
||||
// private static class Dataset extends AbstractMap<String, String> {
|
||||
// private final Attributes attributes;
|
||||
//
|
||||
// private Dataset(Attributes attributes) {
|
||||
// this.attributes = attributes;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Set<Entry<String, String>> entrySet() {
|
||||
// return new EntrySet();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public String put(String key, String value) {
|
||||
// String dataKey = dataKey(key);
|
||||
// String oldValue = attributes.hasKey(dataKey) ? attributes.get(dataKey) : null;
|
||||
// attributes.put(dataKey, value);
|
||||
// return oldValue;
|
||||
// }
|
||||
//
|
||||
// private class EntrySet extends AbstractSet<Map.Entry<String, String>> {
|
||||
//
|
||||
// @Override
|
||||
// public Iterator<Map.Entry<String, String>> iterator() {
|
||||
// return new DatasetIterator();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int size() {
|
||||
// int count = 0;
|
||||
// Iterator iter = new DatasetIterator();
|
||||
// while (iter.hasNext())
|
||||
// count++;
|
||||
// return count;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private class DatasetIterator implements Iterator<Map.Entry<String, String>> {
|
||||
// private Iterator<Attribute> attrIter = attributes.iterator();
|
||||
// private Attribute attr;
|
||||
// public boolean hasNext() {
|
||||
// while (attrIter.hasNext()) {
|
||||
// attr = attrIter.next();
|
||||
// if (attr.isDataAttribute()) return true;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// public Entry<String, String> next() {
|
||||
// return new Attribute(attr.getKey().substring(dataPrefix.length()), attr.getValue());
|
||||
// }
|
||||
//
|
||||
// public void remove() {
|
||||
// attributes.remove(attr.getKey());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private Dataset(Attributes attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, String>> entrySet() {
|
||||
return new EntrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String put(String key, String value) {
|
||||
String dataKey = dataKey(key);
|
||||
String oldValue = attributes.hasKey(dataKey) ? attributes.get(dataKey) : null;
|
||||
attributes.put(dataKey, value);
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
private class EntrySet extends AbstractSet<Map.Entry<String, String>> {
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
return new DatasetIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
int count = 0;
|
||||
Iterator iter = new DatasetIterator();
|
||||
while (iter.hasNext())
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
private class DatasetIterator implements Iterator<Map.Entry<String, String>> {
|
||||
private Iterator<Attribute> attrIter = attributes.iterator();
|
||||
private Attribute attr;
|
||||
public boolean hasNext() {
|
||||
while (attrIter.hasNext()) {
|
||||
attr = attrIter.next();
|
||||
if (attr.isDataAttribute()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Entry<String, String> next() {
|
||||
return new Attribute(attr.getKey().substring(dataPrefix.length()), attr.getValue());
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
attributes.remove(attr.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String dataKey(String key) {
|
||||
return dataPrefix + key;
|
||||
}
|
||||
// private static String dataKey(String key) {
|
||||
// return dataPrefix + key;
|
||||
// }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user