blob: db8d587fde3e3c028f665ba56df1f35b86eb982e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.netbeans.modules.html.editor.lib;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.netbeans.modules.html.editor.lib.api.ProblemDescription;
import org.netbeans.modules.html.editor.lib.api.elements.*;
import org.netbeans.modules.web.common.api.LexerUtils;
import org.openide.util.CharSequences;
/**
*
* @author marekfukala
*/
public class XmlSTElements {
abstract static class ElementBase implements Element {
private CharSequence source;
private int from, to;
private Node parent;
public ElementBase(CharSequence source, int from, int to) {
this.source = source;
this.from = from;
this.to = to;
}
@Override
public int from() {
return from;
}
@Override
public int to() {
return to;
}
@Override
public CharSequence image() {
return source.subSequence(from, to);
}
@Override
public CharSequence id() {
return null;
}
@Override
public Collection<ProblemDescription> problems() {
return Collections.emptyList();
}
void setParent(Node parent) {
this.parent = parent;
}
@Override
public Node parent() {
return parent;
}
@Override
public String toString() {
return new StringBuilder()
.append(type().name())
.append("; ")
.append(from())
.append("-")
.append(to()).toString();
}
}
abstract static class NamedElement extends ElementBase implements Named {
private CharSequence name; //we can possibly use the original source code + pointer
public NamedElement(CharSequence name, CharSequence source, int from, int to) {
super(source, from, to);
this.name = name;
}
@Override
public CharSequence name() {
return name;
}
@Override
public CharSequence namespacePrefix() {
int colonIndex = CharSequences.indexOf(name(), ":");
return colonIndex == -1 ? null : name().subSequence(0, colonIndex);
}
@Override
public CharSequence unqualifiedName() {
int colonIndex = CharSequences.indexOf(name(), ":");
return colonIndex == -1 ? name() : name().subSequence(colonIndex + 1, name().length());
}
@Override
public CharSequence id() {
return name();
}
@Override
public String toString() {
return new StringBuilder().append(name()).append("(").append(type().name()).append(")").append("; ").append(from()).append("-").append(to()).toString();
}
}
static class EmptyOT extends NamedElement implements OpenTag {
private Collection<Attribute> attrs;
public EmptyOT(Collection<Attribute> attrs, CharSequence name, CharSequence source, int from, int to) {
super(name, source, from, to);
this.attrs = attrs;
}
@Override
public Collection<Attribute> attributes() {
return attrs;
}
@Override
public Collection<Attribute> attributes(AttributeFilter filter) {
Collection<Attribute> filtered = new ArrayList<>(1);
for (Attribute a : attributes()) {
if (filter.accepts(a)) {
filtered.add(a);
}
}
return filtered;
}
@Override
public Attribute getAttribute(String name) {
//typically very low number of attrs so the linear search doesn't hurt
for (Attribute a : attributes()) {
if (LexerUtils.equals(name, a.name(), true, false)) {
return a;
}
}
return null;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public CloseTag matchingCloseTag() {
return null;
}
@Override
public int semanticEnd() {
return to();
}
@Override
public Collection<Element> children() {
return Collections.emptyList();
}
@Override
public Collection<Element> children(ElementType type) {
return Collections.emptyList();
}
@Override
public ElementType type() {
return ElementType.OPEN_TAG;
}
@Override
public Collection<Element> children(ElementFilter filter) {
return Collections.emptyList();
}
@Override
public <T extends Element> Collection<T> children(Class<T> type) {
return Collections.emptyList();
}
}
static class OT extends EmptyOT {
private Collection<Element> children;
private CloseTag matchingEndTag;
private int logicalEndOffset;
public OT(Collection<Attribute> attrs, CharSequence name, CharSequence source, int from, int to) {
super(attrs, name, source, from, to);
logicalEndOffset = to;
}
@Override
public boolean isEmpty() {
return false;
}
void setMatchingEndTag(CloseTag endTag) {
this.matchingEndTag = endTag;
}
@Override
public CloseTag matchingCloseTag() {
return matchingEndTag;
}
void addChild(Element child) {
if (children == null) {
children = new ArrayList<>(1);
}
children.add(child);
((ElementBase)child).setParent(this);
}
@Override
public Collection<Element> children() {
return children == null ? Collections.<Element>emptyList() : children;
}
@Override
public Collection<Element> children(ElementType type) {
Collection<Element> filtered = new ArrayList<>();
for (Element e : children()) {
if (e.type() == type) {
filtered.add(e);
}
}
return filtered;
}
@Override
public Collection<Element> children(ElementFilter filter) {
Collection<Element> filtered = new ArrayList<>();
for (Element e : children()) {
if (filter.accepts(e)) {
filtered.add(e);
}
}
return filtered;
}
@Override
public <T extends Element> Collection<T> children(Class<T> type) {
Collection<T> filtered = new ArrayList<>();
for (Element child : children()) {
if (type.isAssignableFrom(child.getClass())) {
filtered.add(type.cast(child));
}
}
return filtered;
}
void setLogicalEndOffset(int to) {
this.logicalEndOffset = to;
}
@Override
public int semanticEnd() {
return logicalEndOffset;
}
}
static class ET extends NamedElement implements CloseTag {
private OpenTag matchingOpenTag;
public ET(CharSequence name, CharSequence source, int from, int to) {
super(name, source, from, to);
}
@Override
public ElementType type() {
return ElementType.CLOSE_TAG;
}
@Override
public OpenTag matchingOpenTag() {
return matchingOpenTag;
}
void setMatchingOpenTag(OpenTag openTag) {
this.matchingOpenTag = openTag;
}
}
public static class Text extends ElementBase {
public Text(CharSequence source, int from, int to) {
super(source, from, to);
}
@Override
public ElementType type() {
return ElementType.TEXT;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" \"")
.append(image())
.append("\"").toString();
}
}
public static class Root extends OT implements FeaturedNode {
private String namespace;
public Root(String namespace, CharSequence source) {
super(Collections.<Attribute>emptyList(), "root", source, 0, source.length());
this.namespace = namespace;
}
@Override
public ElementType type() {
return ElementType.ROOT;
}
@Override
public Object getProperty(String propertyName) {
if (propertyName.equalsIgnoreCase("namespace")) { //NOI18N
return namespace;
}
return null;
}
}
}