1 |
// Jericho HTML Parser - Java based library for analysing and manipulating HTML
|
2 |
// Version 3.2
|
3 |
// Copyright (C) 2004-2009 Martin Jericho
|
4 |
// http://jericho.htmlparser.net/
|
5 |
//
|
6 |
// This library is free software; you can redistribute it and/or
|
7 |
// modify it under the terms of either one of the following licences:
|
8 |
//
|
9 |
// 1. The Eclipse Public License (EPL) version 1.0,
|
10 |
// included in this distribution in the file licence-epl-1.0.html
|
11 |
// or available at http://www.eclipse.org/legal/epl-v10.html
|
12 |
//
|
13 |
// 2. The GNU Lesser General Public License (LGPL) version 2.1 or later,
|
14 |
// included in this distribution in the file licence-lgpl-2.1.txt
|
15 |
// or available at http://www.gnu.org/licenses/lgpl.txt
|
16 |
//
|
17 |
// This library is distributed on an "AS IS" basis,
|
18 |
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
|
19 |
// See the individual licence texts for more details.
|
20 |
|
21 |
package net.htmlparser.jericho;
|
22 |
|
23 |
import java.util.*;
|
24 |
|
25 |
/**
|
26 |
* Provides a generic implementation of the abstract {@link EndTagType} class based on the most common end tag behaviour.
|
27 |
* <p>
|
28 |
* This class is only of interest to users who wish to create <a href="TagType.html#Custom">custom tag types</a>.
|
29 |
* <p>
|
30 |
* The differences between this class and its abstract superclass {@link EndTagType} are:
|
31 |
* <ul>
|
32 |
* <li>The introduction of the {@link #isStatic() IsStatic} property.
|
33 |
* <li>The {@link #constructTagAt(Source, int pos)} method has a default implementation.
|
34 |
* </ul>
|
35 |
* <p>
|
36 |
* Most of the <a href="Tag.html#Predefined">predefined</a> end tag types are implemented using this class or a subclass of it.
|
37 |
*
|
38 |
* @see StartTagTypeGenericImplementation
|
39 |
*/
|
40 |
public class EndTagTypeGenericImplementation extends EndTagType {
|
41 |
private final String staticString;
|
42 |
|
43 |
/**
|
44 |
* Constructs a new <code>EndTagTypeGenericImplementation</code> object based on the specified properties.
|
45 |
* <br />(<a href="TagType.html#ImplementationAssistance">implementation assistance</a> method)
|
46 |
* <p>
|
47 |
* The purpose of the <code>isStatic</code> parameter is explained in the {@link #isStatic() IsStatic} property description.
|
48 |
*
|
49 |
* @param description a {@linkplain #getDescription() description} of the new end tag type useful for debugging purposes.
|
50 |
* @param startDelimiter the {@linkplain #getStartDelimiter() start delimiter} of the new end tag type.
|
51 |
* @param closingDelimiter the {@linkplain #getClosingDelimiter() closing delimiter} of the new end tag type.
|
52 |
* @param isServerTag indicates whether the new end tag type is a {@linkplain #isServerTag() server tag}.
|
53 |
* @param isStatic determines whether the end tag text {@linkplain #isStatic() is static}.
|
54 |
*/
|
55 |
protected EndTagTypeGenericImplementation(final String description, final String startDelimiter, final String closingDelimiter, final boolean isServerTag, final boolean isStatic) {
|
56 |
super(description,startDelimiter,closingDelimiter,isServerTag);
|
57 |
staticString=isStatic ? (startDelimiter+closingDelimiter) : null;
|
58 |
}
|
59 |
|
60 |
/**
|
61 |
* Indicates whether the {@linkplain #generateHTML(String) end tag text} is static.
|
62 |
* <br />(<a href="TagType.html#Property">property</a> and <a href="#ImplementationAssistance">implementation assistance</a> method)
|
63 |
* <p>
|
64 |
* The purpose of this property is to determine the behaviour of the {@link #generateHTML(String startTagName)} method.
|
65 |
* <p>
|
66 |
* If this property is <code>true</code>, the {@linkplain #generateHTML(String) end tag text} is constant for all tags of this type.
|
67 |
* <p>
|
68 |
* If this property is <code>false</code>, the {@linkplain #generateHTML(String) end tag text} includes the
|
69 |
* {@linkplain StartTag#getName() name} of the {@linkplain #getCorrespondingStartTagType corresponding}
|
70 |
* {@linkplain StartTag start tag}.
|
71 |
* <p>
|
72 |
* {@link MasonTagTypes#MASON_COMPONENT_CALLED_WITH_CONTENT_END} is the only <a href="TagType.html#Predefined">predefined</a> end tag
|
73 |
* for which this property is <code>true</code>.
|
74 |
* All tags of this type have the constant tag text "<code></&></code>".
|
75 |
*
|
76 |
* @return <code>true</code> if the {@linkplain #generateHTML(String) end tag text} is static, otherwise <code>false</code>.
|
77 |
*/
|
78 |
protected final boolean isStatic() {
|
79 |
return staticString!=null;
|
80 |
}
|
81 |
|
82 |
/**
|
83 |
* Returns the end tag {@linkplain EndTag#getName() name} that is required to match a {@linkplain #getCorrespondingStartTagType() corresponding} {@linkplain StartTag start tag} with the specified {@linkplain StartTag#getName() name}.
|
84 |
* <br />(<a href="TagType.html#Property">property</a> method)
|
85 |
* <p>
|
86 |
* This implementation overrides the default implementation in {@link EndTagType#getEndTagName(String startTagName)}.
|
87 |
* <p>
|
88 |
* If the value of the {@link #isStatic() IsStatic} property is <code>false</code>, it returns simply returns <code>startTagName</code>, as in the default implementation.
|
89 |
* <p>
|
90 |
* If the value of the {@link #isStatic() IsStatic} property is <code>true</code>, it returns this end tag type's {@linkplain #getNamePrefix() name prefix}.
|
91 |
* <p>
|
92 |
* Note that the <code>startTagName</code> parameter should include the start tag's {@linkplain TagType#getNamePrefix() name prefix} if it has one.
|
93 |
*
|
94 |
* @param startTagName the {@linkplain StartTag#getName() name} of a {@linkplain #getCorrespondingStartTagType() corresponding} {@linkplain StartTag start tag}, including its {@linkplain TagType#getNamePrefix() name prefix}.
|
95 |
* @return the end tag {@linkplain EndTag#getName() name} that is required to match a {@linkplain #getCorrespondingStartTagType() corresponding} {@linkplain StartTag start tag} with the specified {@linkplain StartTag#getName() name}.
|
96 |
*/
|
97 |
public String getEndTagName(final String startTagName) {
|
98 |
return isStatic() ? getNamePrefix() : startTagName;
|
99 |
}
|
100 |
|
101 |
/**
|
102 |
* Generates the HTML text of an {@linkplain EndTag end tag} of this type given the {@linkplain StartTag#getName() name} of a {@linkplain #getCorrespondingStartTagType() corresponding} {@linkplain StartTag start tag}.
|
103 |
* <br />(<a href="TagType.html#Property">property</a> method)
|
104 |
* <p>
|
105 |
* This implementation overrides the default implementation in {@link EndTagType#generateHTML(String startTagName)}
|
106 |
* to improve efficiency in the case of a {@linkplain #isStatic() static} end tag type, although the functionality is the same.
|
107 |
*
|
108 |
* @param startTagName the {@linkplain StartTag#getName() name} of a {@linkplain #getCorrespondingStartTagType() corresponding} {@linkplain StartTag start tag}, including its {@linkplain TagType#getNamePrefix() name prefix}.
|
109 |
* @return the HTML text of an {@linkplain EndTag end tag} of this type given the {@linkplain StartTag#getName() name} of a {@linkplain #getCorrespondingStartTagType() corresponding} {@linkplain StartTag start tag}.
|
110 |
*/
|
111 |
public String generateHTML(final String startTagName) {
|
112 |
return isStatic() ? staticString : super.generateHTML(startTagName);
|
113 |
}
|
114 |
|
115 |
/**
|
116 |
* Constructs a tag of this type at the specified position in the specified source document if it matches all of the required features.
|
117 |
* <br />(<a href="TagType.html#DefaultImplementation">default implementation</a> method)
|
118 |
* <p>
|
119 |
* This default implementation checks the source text for a match according to the following criteria:
|
120 |
* <p>
|
121 |
* If the value of the {@link #isStatic() IsStatic} property is <code>false</code>, this implementation ensures that the
|
122 |
* source text matches the expression:<br />
|
123 |
* {@link #getStartDelimiter() getStartDelimiter()}<code>+"<var>name</var>"+<var>optionalWhiteSpace</var>+</code>{@link #getClosingDelimiter() getClosingDelimiter()}<br />
|
124 |
* where <var>name</var> is a valid {@linkplain Tag#isXMLName(CharSequence) XML tag name}, and <var>optionalWhiteSpace</var> is a string of zero or more {@linkplain Segment#isWhiteSpace(char) white space} characters.
|
125 |
* The {@linkplain Tag#getName() name} of the constructed end tag becomes {@link #getNamePrefix() getNamePrefix()}<code>+"<var>name</var>"</code>.
|
126 |
* <p>
|
127 |
* If the value of the {@link #isStatic() IsStatic} property is <code>true</code>, this implementation ensures that the
|
128 |
* source text matches the static expression:<br />
|
129 |
* {@link #getStartDelimiter() getStartDelimiter()}<code>+</code>{@link #getClosingDelimiter() getClosingDelimiter()}<br />
|
130 |
* The {@linkplain Tag#getName() name} of the constructed end tag is the value of the {@link #getNamePrefix() getNamePrefix()} method.
|
131 |
* <p>
|
132 |
* See {@link TagType#constructTagAt(Source, int pos)} for more important information about this method.
|
133 |
*
|
134 |
* @param source the {@link Source} document.
|
135 |
* @param pos the position in the source document.
|
136 |
* @return a tag of this type at the specified position in the specified source document if it meets all of the required features, or <code>null</code> if it does not meet the criteria.
|
137 |
*/
|
138 |
protected Tag constructTagAt(final Source source, final int pos) {
|
139 |
final ParseText parseText=source.getParseText();
|
140 |
final int nameBegin=pos+START_DELIMITER_PREFIX.length();
|
141 |
String name=null;
|
142 |
final int startDelimiterEnd=pos+getStartDelimiter().length();
|
143 |
int end=-1;
|
144 |
if (isStatic()) {
|
145 |
name=getNamePrefix();
|
146 |
if (!parseText.containsAt(getClosingDelimiter(),startDelimiterEnd)) {
|
147 |
if (source.logger.isInfoEnabled()) source.logger.info(source.getRowColumnVector(pos).appendTo(new StringBuilder(200).append("EndTag of expected format ").append(staticString).append(" at ")).append(" not recognised as type '").append(getDescription()).append("' because it is missing the closing delimiter").toString());
|
148 |
return null;
|
149 |
}
|
150 |
end=startDelimiterEnd+getClosingDelimiter().length();
|
151 |
} else {
|
152 |
final int nameEnd=source.getNameEnd(startDelimiterEnd);
|
153 |
if (nameEnd==-1) return null;
|
154 |
name=source.getName(nameBegin,nameEnd);
|
155 |
int expectedClosingDelimiterPos=nameEnd;
|
156 |
while (Segment.isWhiteSpace(parseText.charAt(expectedClosingDelimiterPos))) expectedClosingDelimiterPos++;
|
157 |
if (!parseText.containsAt(getClosingDelimiter(),expectedClosingDelimiterPos)) {
|
158 |
if (source.logger.isInfoEnabled()) source.logger.info(source.getRowColumnVector(pos).appendTo(new StringBuilder(200).append("EndTag ").append(name).append(" at ")).append(" not recognised as type '").append(getDescription()).append("' because its name and closing delimiter are separated by characters other than white space").toString());
|
159 |
return null;
|
160 |
}
|
161 |
end=expectedClosingDelimiterPos+getClosingDelimiter().length();
|
162 |
}
|
163 |
return constructEndTag(source,pos,end,name);
|
164 |
}
|
165 |
}
|