From 54b1f140f12e213fa2a677d58e892d8f33bb4061 Mon Sep 17 00:00:00 2001 From: Zheng Sun Date: Sat, 16 Feb 2013 15:33:44 +0800 Subject: [PATCH] Use jar instead of source code --- .classpath | 1 - libs/htmlcleaner.jar | Bin 0 -> 93718 bytes src/org/htmlcleaner/BaseToken.java | 67 -- .../BrowserCompactXmlSerializer.java | 120 --- src/org/htmlcleaner/CleanerProperties.java | 277 ------ .../htmlcleaner/CleanerTransformations.java | 84 -- src/org/htmlcleaner/CommentNode.java | 86 -- .../htmlcleaner/CompactHtmlSerializer.java | 131 --- src/org/htmlcleaner/CompactXmlSerializer.java | 100 -- src/org/htmlcleaner/ContentNode.java | 88 -- src/org/htmlcleaner/DefaultTagProvider.java | 522 ---------- src/org/htmlcleaner/DoctypeToken.java | 146 --- src/org/htmlcleaner/DomSerializer.java | 157 --- src/org/htmlcleaner/EndTagToken.java | 80 -- src/org/htmlcleaner/FastHtmlSerializer.java | 64 -- src/org/htmlcleaner/HtmlCleaner.java | 932 ------------------ src/org/htmlcleaner/HtmlCleanerException.java | 79 -- src/org/htmlcleaner/HtmlNode.java | 23 - src/org/htmlcleaner/HtmlSerializer.java | 237 ----- src/org/htmlcleaner/HtmlTokenizer.java | 836 ---------------- src/org/htmlcleaner/ITagInfoProvider.java | 66 -- src/org/htmlcleaner/PrettyHtmlSerializer.java | 233 ----- src/org/htmlcleaner/Serializer.java | 338 ------- src/org/htmlcleaner/SimpleHtmlSerializer.java | 86 -- src/org/htmlcleaner/SimpleXmlSerializer.java | 87 -- src/org/htmlcleaner/SpecialEntity.java | 398 -------- src/org/htmlcleaner/TagInfo.java | 406 -------- src/org/htmlcleaner/TagNode.java | 765 -------------- src/org/htmlcleaner/TagNodeVisitor.java | 36 - src/org/htmlcleaner/TagToken.java | 81 -- src/org/htmlcleaner/TagTransformation.java | 164 --- src/org/htmlcleaner/Utils.java | 524 ---------- src/org/htmlcleaner/XPather.java | 626 ------------ src/org/htmlcleaner/XPatherException.java | 80 -- src/org/htmlcleaner/XmlSerializer.java | 254 ----- 35 files changed, 8174 deletions(-) create mode 100644 libs/htmlcleaner.jar delete mode 100644 src/org/htmlcleaner/BaseToken.java delete mode 100644 src/org/htmlcleaner/BrowserCompactXmlSerializer.java delete mode 100644 src/org/htmlcleaner/CleanerProperties.java delete mode 100644 src/org/htmlcleaner/CleanerTransformations.java delete mode 100644 src/org/htmlcleaner/CommentNode.java delete mode 100644 src/org/htmlcleaner/CompactHtmlSerializer.java delete mode 100644 src/org/htmlcleaner/CompactXmlSerializer.java delete mode 100644 src/org/htmlcleaner/ContentNode.java delete mode 100644 src/org/htmlcleaner/DefaultTagProvider.java delete mode 100644 src/org/htmlcleaner/DoctypeToken.java delete mode 100644 src/org/htmlcleaner/DomSerializer.java delete mode 100644 src/org/htmlcleaner/EndTagToken.java delete mode 100644 src/org/htmlcleaner/FastHtmlSerializer.java delete mode 100644 src/org/htmlcleaner/HtmlCleaner.java delete mode 100644 src/org/htmlcleaner/HtmlCleanerException.java delete mode 100644 src/org/htmlcleaner/HtmlNode.java delete mode 100644 src/org/htmlcleaner/HtmlSerializer.java delete mode 100644 src/org/htmlcleaner/HtmlTokenizer.java delete mode 100644 src/org/htmlcleaner/ITagInfoProvider.java delete mode 100644 src/org/htmlcleaner/PrettyHtmlSerializer.java delete mode 100644 src/org/htmlcleaner/Serializer.java delete mode 100644 src/org/htmlcleaner/SimpleHtmlSerializer.java delete mode 100644 src/org/htmlcleaner/SimpleXmlSerializer.java delete mode 100644 src/org/htmlcleaner/SpecialEntity.java delete mode 100644 src/org/htmlcleaner/TagInfo.java delete mode 100644 src/org/htmlcleaner/TagNode.java delete mode 100644 src/org/htmlcleaner/TagNodeVisitor.java delete mode 100644 src/org/htmlcleaner/TagToken.java delete mode 100644 src/org/htmlcleaner/TagTransformation.java delete mode 100644 src/org/htmlcleaner/Utils.java delete mode 100644 src/org/htmlcleaner/XPather.java delete mode 100644 src/org/htmlcleaner/XPatherException.java delete mode 100644 src/org/htmlcleaner/XmlSerializer.java diff --git a/.classpath b/.classpath index 855eb6a..ba16b1f 100644 --- a/.classpath +++ b/.classpath @@ -6,6 +6,5 @@ - diff --git a/libs/htmlcleaner.jar b/libs/htmlcleaner.jar new file mode 100644 index 0000000000000000000000000000000000000000..9ef85b34456feacfc0a817c0660f713b3279d0a3 GIT binary patch literal 93718 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�zdhY8 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 AkN^Mx literal 0 HcmV?d00001 diff --git a/src/org/htmlcleaner/BaseToken.java b/src/org/htmlcleaner/BaseToken.java deleted file mode 100644 index 200aae0..0000000 --- a/src/org/htmlcleaner/BaseToken.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; - -/** - *

- * Base token interface. Tokens are individual entities recognized by HTML - * parser. - *

- */ -public interface BaseToken { - void serialize(Serializer serializer, Writer writer) throws IOException; -} diff --git a/src/org/htmlcleaner/BrowserCompactXmlSerializer.java b/src/org/htmlcleaner/BrowserCompactXmlSerializer.java deleted file mode 100644 index ee5e518..0000000 --- a/src/org/htmlcleaner/BrowserCompactXmlSerializer.java +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.Writer; -import java.io.IOException; -import java.util.List; -import java.util.ListIterator; - -/** - *

- * Broswer compact XML serializer - creates resulting XML by stripping - * whitespaces wherever possible, but preserving single whitespace where at - * least one exists. This behaviour is well suited for web-browsers, which - * usualy treat multiple whitespaces as single one, but make diffrence between - * single whitespace and empty text. - *

- */ -public class BrowserCompactXmlSerializer extends XmlSerializer { - public BrowserCompactXmlSerializer(final CleanerProperties props) { - super(props); - } - - protected void serialize(final TagNode tagNode, final Writer writer) throws IOException { - serializeOpenTag(tagNode, writer, false); - - final List tagChildren = tagNode.getChildren(); - if (!isMinimizedTagSyntax(tagNode)) { - final ListIterator childrenIt = tagChildren.listIterator(); - while (childrenIt.hasNext()) { - final Object item = childrenIt.next(); - if (item instanceof ContentNode) { - String content = item.toString(); - final boolean startsWithSpace = content.length() > 0 && Character.isWhitespace(content.charAt(0)); - final boolean endsWithSpace = content.length() > 1 - && Character.isWhitespace(content.charAt(content.length() - 1)); - content = dontEscape(tagNode) ? content.trim().replaceAll("]]>", "]]>") : escapeXml(content - .trim()); - - if (startsWithSpace) { - writer.write(' '); - } - - if (content.length() != 0) { - writer.write(content); - if (endsWithSpace) { - writer.write(' '); - } - } - - if (childrenIt.hasNext()) { - if (!Utils.isWhitespaceString(childrenIt.next())) { - writer.write("\n"); - } - childrenIt.previous(); - } - } else if (item instanceof CommentNode) { - final String content = ((CommentNode) item).getCommentedContent().trim(); - writer.write(content); - } else if (item instanceof BaseToken) { - ((BaseToken) item).serialize(this, writer); - } - } - - serializeEndTag(tagNode, writer, false); - } - } - -} diff --git a/src/org/htmlcleaner/CleanerProperties.java b/src/org/htmlcleaner/CleanerProperties.java deleted file mode 100644 index 48d5de3..0000000 --- a/src/org/htmlcleaner/CleanerProperties.java +++ /dev/null @@ -1,277 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -/** - * Properties defining cleaner's behaviour - */ -public class CleanerProperties { - public static final String BOOL_ATT_SELF = "self"; - public static final String BOOL_ATT_EMPTY = "empty"; - public static final String BOOL_ATT_TRUE = "true"; - - private ITagInfoProvider tagInfoProvider = null; - private boolean advancedXmlEscape = true; - private boolean transResCharsToNCR = false; - private boolean useCdataForScriptAndStyle = true; - private boolean translateSpecialEntities = true; - private boolean transSpecialEntitiesToNCR = false; - private boolean recognizeUnicodeChars = true; - private boolean omitUnknownTags = false; - private boolean treatUnknownTagsAsContent = false; - private boolean omitDeprecatedTags = false; - private boolean treatDeprecatedTagsAsContent = false; - private boolean omitComments = false; - private boolean omitXmlDeclaration = false; - private boolean omitDoctypeDeclaration = true; - private boolean omitHtmlEnvelope = false; - private boolean useEmptyElementTags = true; - private boolean allowMultiWordAttributes = true; - private boolean allowHtmlInsideAttributes = false; - private boolean ignoreQuestAndExclam = true; - private boolean namespacesAware = true; - private String hyphenReplacementInComment = "="; - private String booleanAttributeValues = BOOL_ATT_SELF; - private String pruneTags = null; - - public String getBooleanAttributeValues() { - return booleanAttributeValues; - } - - public String getHyphenReplacementInComment() { - return hyphenReplacementInComment; - } - - public String getPruneTags() { - return pruneTags; - } - - public ITagInfoProvider getTagInfoProvider() { - return tagInfoProvider; - } - - public boolean isAdvancedXmlEscape() { - return advancedXmlEscape; - } - - public boolean isAllowHtmlInsideAttributes() { - return allowHtmlInsideAttributes; - } - - public boolean isAllowMultiWordAttributes() { - return allowMultiWordAttributes; - } - - public boolean isIgnoreQuestAndExclam() { - return ignoreQuestAndExclam; - } - - public boolean isNamespacesAware() { - return namespacesAware; - } - - public boolean isOmitComments() { - return omitComments; - } - - public boolean isOmitDeprecatedTags() { - return omitDeprecatedTags; - } - - public boolean isOmitDoctypeDeclaration() { - return omitDoctypeDeclaration; - } - - public boolean isOmitHtmlEnvelope() { - return omitHtmlEnvelope; - } - - public boolean isOmitUnknownTags() { - return omitUnknownTags; - } - - public boolean isOmitXmlDeclaration() { - return omitXmlDeclaration; - } - - public boolean isRecognizeUnicodeChars() { - return recognizeUnicodeChars; - } - - public boolean isTranslateSpecialEntities() { - return translateSpecialEntities; - } - - public boolean isTransResCharsToNCR() { - return transResCharsToNCR; - } - - public boolean isTransSpecialEntitiesToNCR() { - return transSpecialEntitiesToNCR; - } - - public boolean isTreatDeprecatedTagsAsContent() { - return treatDeprecatedTagsAsContent; - } - - public boolean isTreatUnknownTagsAsContent() { - return treatUnknownTagsAsContent; - } - - public boolean isUseCdataForScriptAndStyle() { - return useCdataForScriptAndStyle; - } - - public boolean isUseEmptyElementTags() { - return useEmptyElementTags; - } - - public void setAdvancedXmlEscape(final boolean advancedXmlEscape) { - this.advancedXmlEscape = advancedXmlEscape; - } - - public void setAllowHtmlInsideAttributes(final boolean allowHtmlInsideAttributes) { - this.allowHtmlInsideAttributes = allowHtmlInsideAttributes; - } - - public void setAllowMultiWordAttributes(final boolean allowMultiWordAttributes) { - this.allowMultiWordAttributes = allowMultiWordAttributes; - } - - public void setBooleanAttributeValues(final String booleanAttributeValues) { - if (BOOL_ATT_SELF.equalsIgnoreCase(booleanAttributeValues) - || BOOL_ATT_EMPTY.equalsIgnoreCase(booleanAttributeValues) - || BOOL_ATT_TRUE.equalsIgnoreCase(booleanAttributeValues)) { - this.booleanAttributeValues = booleanAttributeValues.toLowerCase(); - } else { - this.booleanAttributeValues = BOOL_ATT_SELF; - } - } - - public void setHyphenReplacementInComment(final String hyphenReplacementInComment) { - this.hyphenReplacementInComment = hyphenReplacementInComment; - } - - public void setIgnoreQuestAndExclam(final boolean ignoreQuestAndExclam) { - this.ignoreQuestAndExclam = ignoreQuestAndExclam; - } - - public void setNamespacesAware(final boolean namespacesAware) { - this.namespacesAware = namespacesAware; - } - - public void setOmitComments(final boolean omitComments) { - this.omitComments = omitComments; - } - - public void setOmitDeprecatedTags(final boolean omitDeprecatedTags) { - this.omitDeprecatedTags = omitDeprecatedTags; - } - - public void setOmitDoctypeDeclaration(final boolean omitDoctypeDeclaration) { - this.omitDoctypeDeclaration = omitDoctypeDeclaration; - } - - public void setOmitHtmlEnvelope(final boolean omitHtmlEnvelope) { - this.omitHtmlEnvelope = omitHtmlEnvelope; - } - - public void setOmitUnknownTags(final boolean omitUnknownTags) { - this.omitUnknownTags = omitUnknownTags; - } - - public void setOmitXmlDeclaration(final boolean omitXmlDeclaration) { - this.omitXmlDeclaration = omitXmlDeclaration; - } - - public void setPruneTags(final String pruneTags) { - this.pruneTags = pruneTags; - } - - public void setRecognizeUnicodeChars(final boolean recognizeUnicodeChars) { - this.recognizeUnicodeChars = recognizeUnicodeChars; - } - - public void setTagInfoProvider(final ITagInfoProvider tagInfoProvider) { - this.tagInfoProvider = tagInfoProvider; - } - - public void setTranslateSpecialEntities(final boolean translateSpecialEntities) { - this.translateSpecialEntities = translateSpecialEntities; - } - - public void setTransResCharsToNCR(final boolean transResCharsToNCR) { - this.transResCharsToNCR = transResCharsToNCR; - } - - public void setTransSpecialEntitiesToNCR(final boolean transSpecialEntitiesToNCR) { - this.transSpecialEntitiesToNCR = transSpecialEntitiesToNCR; - } - - public void setTreatDeprecatedTagsAsContent(final boolean treatDeprecatedTagsAsContent) { - this.treatDeprecatedTagsAsContent = treatDeprecatedTagsAsContent; - } - - public void setTreatUnknownTagsAsContent(final boolean treatUnknownTagsAsContent) { - this.treatUnknownTagsAsContent = treatUnknownTagsAsContent; - } - - public void setUseCdataForScriptAndStyle(final boolean useCdataForScriptAndStyle) { - this.useCdataForScriptAndStyle = useCdataForScriptAndStyle; - } - - public void setUseEmptyElementTags(final boolean useEmptyElementTags) { - this.useEmptyElementTags = useEmptyElementTags; - } -} diff --git a/src/org/htmlcleaner/CleanerTransformations.java b/src/org/htmlcleaner/CleanerTransformations.java deleted file mode 100644 index d2aa509..0000000 --- a/src/org/htmlcleaner/CleanerTransformations.java +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.util.HashMap; -import java.util.Map; - -/** - * Contains transformation collection. - */ -public class CleanerTransformations { - - final private Map mappings = new HashMap(); - - /** - * Adds specified tag transformation to the collection. - * - * @param tagTransformation - */ - public void addTransformation(final TagTransformation tagTransformation) { - if (tagTransformation != null) { - mappings.put(tagTransformation.getSourceTag(), tagTransformation); - } - } - - public TagTransformation getTransformation(final String tagName) { - return tagName == null ? null : mappings.get(tagName.toLowerCase()); - } - - public boolean hasTransformationForTag(final String tagName) { - return tagName != null && mappings.containsKey(tagName.toLowerCase()); - } -} diff --git a/src/org/htmlcleaner/CommentNode.java b/src/org/htmlcleaner/CommentNode.java deleted file mode 100644 index 398850a..0000000 --- a/src/org/htmlcleaner/CommentNode.java +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; - -/** - *

- * HTML comment token. - *

- */ -public class CommentNode implements BaseToken, HtmlNode { - final private StringBuilder content; - - public CommentNode(final String content) { - this.content = new StringBuilder(content); - } - - public String getCommentedContent() { - return ""; - } - - public StringBuilder getContent() { - return content; - } - - public void serialize(final Serializer serializer, final Writer writer) throws IOException { - writer.write(getCommentedContent()); - } - - public String toString() { - return getCommentedContent(); - } -} diff --git a/src/org/htmlcleaner/CompactHtmlSerializer.java b/src/org/htmlcleaner/CompactHtmlSerializer.java deleted file mode 100644 index b639e75..0000000 --- a/src/org/htmlcleaner/CompactHtmlSerializer.java +++ /dev/null @@ -1,131 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; -import java.util.List; -import java.util.ListIterator; - -/** - *

- * Compact HTML serializer - creates resulting HTML by stripping whitespaces - * wherever possible. - *

- */ -public class CompactHtmlSerializer extends HtmlSerializer { - private int openPreTags = 0; - - public CompactHtmlSerializer(final CleanerProperties props) { - super(props); - } - - protected void serialize(final TagNode tagNode, final Writer writer) throws IOException { - final boolean isPreTag = "pre".equalsIgnoreCase(tagNode.getName()); - if (isPreTag) { - openPreTags++; - } - - serializeOpenTag(tagNode, writer, false); - - final List tagChildren = tagNode.getChildren(); - if (!isMinimizedTagSyntax(tagNode)) { - final ListIterator childrenIt = tagChildren.listIterator(); - while (childrenIt.hasNext()) { - final Object item = childrenIt.next(); - if (item instanceof ContentNode) { - String content = item.toString(); - if (openPreTags > 0) { - writer.write(content); - } else { - final boolean startsWithSpace = content.length() > 0 - && Character.isWhitespace(content.charAt(0)); - final boolean endsWithSpace = content.length() > 1 - && Character.isWhitespace(content.charAt(content.length() - 1)); - content = dontEscape(tagNode) ? content.trim() : escapeText(content.trim()); - - if (startsWithSpace) { - writer.write(' '); - } - - if (content.length() != 0) { - writer.write(content); - if (endsWithSpace) { - writer.write(' '); - } - } - - if (childrenIt.hasNext()) { - if (!Utils.isWhitespaceString(childrenIt.next())) { - writer.write("\n"); - } - childrenIt.previous(); - } - } - } else if (item instanceof CommentNode) { - final String content = ((CommentNode) item).getCommentedContent().trim(); - writer.write(content); - } else if (item instanceof BaseToken) { - ((BaseToken) item).serialize(this, writer); - } - } - - serializeEndTag(tagNode, writer, false); - if (isPreTag) { - openPreTags--; - } - } - } - -} diff --git a/src/org/htmlcleaner/CompactXmlSerializer.java b/src/org/htmlcleaner/CompactXmlSerializer.java deleted file mode 100644 index 72221bf..0000000 --- a/src/org/htmlcleaner/CompactXmlSerializer.java +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; -import java.util.*; - -/** - *

- * Compact XML serializer - creates resulting XML by stripping whitespaces. - *

- */ -public class CompactXmlSerializer extends XmlSerializer { - public CompactXmlSerializer(final CleanerProperties props) { - super(props); - } - - protected void serialize(final TagNode tagNode, final Writer writer) throws IOException { - serializeOpenTag(tagNode, writer, false); - - final List tagChildren = tagNode.getChildren(); - if (!isMinimizedTagSyntax(tagNode)) { - final ListIterator childrenIt = tagChildren.listIterator(); - while (childrenIt.hasNext()) { - final Object item = childrenIt.next(); - if (item instanceof ContentNode) { - final String content = item.toString().trim(); - writer.write(dontEscape(tagNode) ? content.replaceAll("]]>", "]]>") : escapeXml(content)); - - if (childrenIt.hasNext()) { - if (!Utils.isWhitespaceString(childrenIt.next())) { - writer.write("\n"); - } - childrenIt.previous(); - } - } else if (item instanceof CommentNode) { - final String content = ((CommentNode) item).getCommentedContent().trim(); - writer.write(content); - } else if (item instanceof BaseToken) { - ((BaseToken) item).serialize(this, writer); - } - } - - serializeEndTag(tagNode, writer, false); - } - } - -} diff --git a/src/org/htmlcleaner/ContentNode.java b/src/org/htmlcleaner/ContentNode.java deleted file mode 100644 index 382d876..0000000 --- a/src/org/htmlcleaner/ContentNode.java +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; - -/** - *

- * HTML text token. - *

- */ -public class ContentNode implements BaseToken, HtmlNode { - final private StringBuilder content; - - public ContentNode(final char content[], final int len) { - this.content = new StringBuilder(len + 16); - this.content.append(content, 0, len); - } - - public ContentNode(final String content) { - this.content = new StringBuilder(content); - } - - public StringBuilder getContent() { - return content; - } - - public void serialize(final Serializer serializer, final Writer writer) throws IOException { - writer.write(content.toString()); - } - - public String toString() { - return content.toString(); - } - -} diff --git a/src/org/htmlcleaner/DefaultTagProvider.java b/src/org/htmlcleaner/DefaultTagProvider.java deleted file mode 100644 index 445e783..0000000 --- a/src/org/htmlcleaner/DefaultTagProvider.java +++ /dev/null @@ -1,522 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.util.HashMap; - -/** - * This class is automatically created from ConfigFileTagProvider which reads - * default XML configuration file with tag descriptions. It is used as default - * tag info provider. Class is created for performance purposes - parsing XML - * file requires some processing time. - */ -public class DefaultTagProvider extends HashMap implements ITagInfoProvider { - private static final long serialVersionUID = 1L; - - // singleton instance, used if no other TagInfoProvider is specified - private static DefaultTagProvider instance; - - /** - * @return Singleton instance of this class. - */ - public static synchronized DefaultTagProvider getInstance() { - if (instance == null) { - instance = new DefaultTagProvider(); - } - return instance; - } - - public DefaultTagProvider() { - super(); - - TagInfo tagInfo; - - tagInfo = new TagInfo("div", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("div", tagInfo); - - tagInfo = new TagInfo("span", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("span", tagInfo); - - tagInfo = new TagInfo("meta", TagInfo.CONTENT_NONE, TagInfo.HEAD, false, false, false); - this.put("meta", tagInfo); - - tagInfo = new TagInfo("link", TagInfo.CONTENT_NONE, TagInfo.HEAD, false, false, false); - this.put("link", tagInfo); - - tagInfo = new TagInfo("title", TagInfo.CONTENT_TEXT, TagInfo.HEAD, false, true, false); - this.put("title", tagInfo); - - tagInfo = new TagInfo("style", TagInfo.CONTENT_TEXT, TagInfo.HEAD, false, false, false); - this.put("style", tagInfo); - - tagInfo = new TagInfo("bgsound", TagInfo.CONTENT_NONE, TagInfo.HEAD, false, false, false); - this.put("bgsound", tagInfo); - - tagInfo = new TagInfo("h1", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("h1,h2,h3,h4,h5,h6,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("h1", tagInfo); - - tagInfo = new TagInfo("h2", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("h1,h2,h3,h4,h5,h6,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("h2", tagInfo); - - tagInfo = new TagInfo("h3", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("h1,h2,h3,h4,h5,h6,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("h3", tagInfo); - - tagInfo = new TagInfo("h4", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("h1,h2,h3,h4,h5,h6,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("h4", tagInfo); - - tagInfo = new TagInfo("h5", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("h1,h2,h3,h4,h5,h6,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("h5", tagInfo); - - tagInfo = new TagInfo("h6", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("h1,h2,h3,h4,h5,h6,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("h6", tagInfo); - - tagInfo = new TagInfo("p", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("p", tagInfo); - - tagInfo = new TagInfo("strong", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("strong", tagInfo); - - tagInfo = new TagInfo("em", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("em", tagInfo); - - tagInfo = new TagInfo("abbr", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("abbr", tagInfo); - - tagInfo = new TagInfo("acronym", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("acronym", tagInfo); - - tagInfo = new TagInfo("address", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("address", tagInfo); - - tagInfo = new TagInfo("bdo", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("bdo", tagInfo); - - tagInfo = new TagInfo("blockquote", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("blockquote", tagInfo); - - tagInfo = new TagInfo("cite", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("cite", tagInfo); - - tagInfo = new TagInfo("q", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("q", tagInfo); - - tagInfo = new TagInfo("code", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("code", tagInfo); - - tagInfo = new TagInfo("ins", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("ins", tagInfo); - - tagInfo = new TagInfo("del", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("del", tagInfo); - - tagInfo = new TagInfo("dfn", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("dfn", tagInfo); - - tagInfo = new TagInfo("kbd", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("kbd", tagInfo); - - tagInfo = new TagInfo("pre", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("pre", tagInfo); - - tagInfo = new TagInfo("samp", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("samp", tagInfo); - - tagInfo = new TagInfo("listing", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("listing", tagInfo); - - tagInfo = new TagInfo("var", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("var", tagInfo); - - tagInfo = new TagInfo("br", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - this.put("br", tagInfo); - - tagInfo = new TagInfo("wbr", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - this.put("wbr", tagInfo); - - tagInfo = new TagInfo("nobr", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("nobr"); - this.put("nobr", tagInfo); - - tagInfo = new TagInfo("xmp", TagInfo.CONTENT_TEXT, TagInfo.BODY, false, false, false); - this.put("xmp", tagInfo); - - tagInfo = new TagInfo("a", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("a"); - this.put("a", tagInfo); - - tagInfo = new TagInfo("base", TagInfo.CONTENT_NONE, TagInfo.HEAD, false, false, false); - this.put("base", tagInfo); - - tagInfo = new TagInfo("img", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - this.put("img", tagInfo); - - tagInfo = new TagInfo("area", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("map"); - tagInfo.defineCloseBeforeTags("area"); - this.put("area", tagInfo); - - tagInfo = new TagInfo("map", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("map"); - this.put("map", tagInfo); - - tagInfo = new TagInfo("object", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("object", tagInfo); - - tagInfo = new TagInfo("param", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("param", tagInfo); - - tagInfo = new TagInfo("applet", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - this.put("applet", tagInfo); - - tagInfo = new TagInfo("xml", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("xml", tagInfo); - - tagInfo = new TagInfo("ul", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("ul", tagInfo); - - tagInfo = new TagInfo("ol", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("ol", tagInfo); - - tagInfo = new TagInfo("li", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("li,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("li", tagInfo); - - tagInfo = new TagInfo("dl", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("dl", tagInfo); - - tagInfo = new TagInfo("dt", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("dt,dd"); - this.put("dt", tagInfo); - - tagInfo = new TagInfo("dd", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("dt,dd"); - this.put("dd", tagInfo); - - tagInfo = new TagInfo("menu", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("menu", tagInfo); - - tagInfo = new TagInfo("dir", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("dir", tagInfo); - - tagInfo = new TagInfo("table", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineAllowedChildrenTags("tr,tbody,thead,tfoot,colgroup,col,form,caption,tr"); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("tr,thead,tbody,tfoot,caption,colgroup,table,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param"); - this.put("table", tagInfo); - - tagInfo = new TagInfo("tr", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineRequiredEnclosingTags("tbody"); - tagInfo.defineAllowedChildrenTags("td,th"); - tagInfo.defineHigherLevelTags("thead,tfoot"); - tagInfo.defineCloseBeforeTags("tr,td,th,caption,colgroup"); - this.put("tr", tagInfo); - - tagInfo = new TagInfo("td", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineRequiredEnclosingTags("tr"); - tagInfo.defineCloseBeforeTags("td,th,caption,colgroup"); - this.put("td", tagInfo); - - tagInfo = new TagInfo("th", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineRequiredEnclosingTags("tr"); - tagInfo.defineCloseBeforeTags("td,th,caption,colgroup"); - this.put("th", tagInfo); - - tagInfo = new TagInfo("tbody", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineAllowedChildrenTags("tr,form"); - tagInfo.defineCloseBeforeTags("td,th,tr,tbody,thead,tfoot,caption,colgroup"); - this.put("tbody", tagInfo); - - tagInfo = new TagInfo("thead", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineAllowedChildrenTags("tr,form"); - tagInfo.defineCloseBeforeTags("td,th,tr,tbody,thead,tfoot,caption,colgroup"); - this.put("thead", tagInfo); - - tagInfo = new TagInfo("tfoot", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineAllowedChildrenTags("tr,form"); - tagInfo.defineCloseBeforeTags("td,th,tr,tbody,thead,tfoot,caption,colgroup"); - this.put("tfoot", tagInfo); - - tagInfo = new TagInfo("col", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - this.put("col", tagInfo); - - tagInfo = new TagInfo("colgroup", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineAllowedChildrenTags("col"); - tagInfo.defineCloseBeforeTags("td,th,tr,tbody,thead,tfoot,caption,colgroup"); - this.put("colgroup", tagInfo); - - tagInfo = new TagInfo("caption", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineFatalTags("table"); - tagInfo.defineCloseBeforeTags("td,th,tr,tbody,thead,tfoot,caption,colgroup"); - this.put("caption", tagInfo); - - tagInfo = new TagInfo("form", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, true); - tagInfo.defineForbiddenTags("form"); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo - .defineCloseBeforeTags("option,optgroup,textarea,select,fieldset,p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("form", tagInfo); - - tagInfo = new TagInfo("input", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("select,optgroup,option"); - this.put("input", tagInfo); - - tagInfo = new TagInfo("textarea", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("select,optgroup,option"); - this.put("textarea", tagInfo); - - tagInfo = new TagInfo("select", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, true); - tagInfo.defineAllowedChildrenTags("option,optgroup"); - tagInfo.defineCloseBeforeTags("option,optgroup,select"); - this.put("select", tagInfo); - - tagInfo = new TagInfo("option", TagInfo.CONTENT_TEXT, TagInfo.BODY, false, false, true); - tagInfo.defineFatalTags("select"); - tagInfo.defineCloseBeforeTags("option"); - this.put("option", tagInfo); - - tagInfo = new TagInfo("optgroup", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, true); - tagInfo.defineFatalTags("select"); - tagInfo.defineAllowedChildrenTags("option"); - tagInfo.defineCloseBeforeTags("optgroup"); - this.put("optgroup", tagInfo); - - tagInfo = new TagInfo("button", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeTags("select,optgroup,option"); - this.put("button", tagInfo); - - tagInfo = new TagInfo("label", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("label", tagInfo); - - tagInfo = new TagInfo("fieldset", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("fieldset", tagInfo); - - tagInfo = new TagInfo("legend", TagInfo.CONTENT_TEXT, TagInfo.BODY, false, false, false); - tagInfo.defineRequiredEnclosingTags("fieldset"); - tagInfo.defineCloseBeforeTags("legend"); - this.put("legend", tagInfo); - - tagInfo = new TagInfo("isindex", TagInfo.CONTENT_NONE, TagInfo.BODY, true, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("isindex", tagInfo); - - tagInfo = new TagInfo("script", TagInfo.CONTENT_ALL, TagInfo.HEAD_AND_BODY, false, false, false); - this.put("script", tagInfo); - - tagInfo = new TagInfo("noscript", TagInfo.CONTENT_ALL, TagInfo.HEAD_AND_BODY, false, false, false); - this.put("noscript", tagInfo); - - tagInfo = new TagInfo("b", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("u,i,tt,sub,sup,big,small,strike,blink,s"); - this.put("b", tagInfo); - - tagInfo = new TagInfo("i", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,tt,sub,sup,big,small,strike,blink,s"); - this.put("i", tagInfo); - - tagInfo = new TagInfo("u", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,i,tt,sub,sup,big,small,strike,blink,s"); - this.put("u", tagInfo); - - tagInfo = new TagInfo("tt", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,sub,sup,big,small,strike,blink,s"); - this.put("tt", tagInfo); - - tagInfo = new TagInfo("sub", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,tt,sup,big,small,strike,blink,s"); - this.put("sub", tagInfo); - - tagInfo = new TagInfo("sup", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,tt,sub,big,small,strike,blink,s"); - this.put("sup", tagInfo); - - tagInfo = new TagInfo("big", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,tt,sub,sup,small,strike,blink,s"); - this.put("big", tagInfo); - - tagInfo = new TagInfo("small", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,tt,sub,sup,big,strike,blink,s"); - this.put("small", tagInfo); - - tagInfo = new TagInfo("strike", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,tt,sub,sup,big,small,blink,s"); - this.put("strike", tagInfo); - - tagInfo = new TagInfo("blink", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,tt,sub,sup,big,small,strike,s"); - this.put("blink", tagInfo); - - tagInfo = new TagInfo("marquee", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("marquee", tagInfo); - - tagInfo = new TagInfo("s", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - tagInfo.defineCloseInsideCopyAfterTags("b,u,i,tt,sub,sup,big,small,strike,blink"); - this.put("s", tagInfo); - - tagInfo = new TagInfo("hr", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("hr", tagInfo); - - tagInfo = new TagInfo("font", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - this.put("font", tagInfo); - - tagInfo = new TagInfo("basefont", TagInfo.CONTENT_NONE, TagInfo.BODY, true, false, false); - this.put("basefont", tagInfo); - - tagInfo = new TagInfo("center", TagInfo.CONTENT_ALL, TagInfo.BODY, true, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("center", tagInfo); - - tagInfo = new TagInfo("comment", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("comment", tagInfo); - - tagInfo = new TagInfo("server", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("server", tagInfo); - - tagInfo = new TagInfo("iframe", TagInfo.CONTENT_ALL, TagInfo.BODY, false, false, false); - this.put("iframe", tagInfo); - - tagInfo = new TagInfo("embed", TagInfo.CONTENT_NONE, TagInfo.BODY, false, false, false); - tagInfo.defineCloseBeforeCopyInsideTags("a,bdo,strong,em,q,b,i,u,tt,sub,sup,big,small,strike,s,font"); - tagInfo.defineCloseBeforeTags("p,address,label,abbr,acronym,dfn,kbd,samp,var,cite,code,param,xml"); - this.put("embed", tagInfo); - } - - /** - * Sets new tag info. - * - * @param tagInfo - * tag info to be added to the provider. - */ - public void addTagInfo(TagInfo tagInfo) { - if (tagInfo != null) { - put(tagInfo.getName().toLowerCase(), tagInfo); - } - } - - public TagInfo getTagInfo(String tagName) { - return get(tagName); - } - - /** - * Removes tag info with specified name. - * - * @param tagName - * Name of the tag to be removed from the tag provider. - */ - public void removeTagInfo(String tagName) { - if (tagName != null) { - remove(tagName.toLowerCase()); - } - } - -} diff --git a/src/org/htmlcleaner/DoctypeToken.java b/src/org/htmlcleaner/DoctypeToken.java deleted file mode 100644 index a84616e..0000000 --- a/src/org/htmlcleaner/DoctypeToken.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; - -/** - *

- * HTML doctype token. - *

- */ -public class DoctypeToken implements BaseToken { - private static String clean(String s) { - if (s != null) { - s = s.replace('>', ' '); - s = s.replace('<', ' '); - s = s.replace('&', ' '); - s = s.replace('\'', ' '); - s = s.replace('\"', ' '); - } - - return s; - } - - final private String part1; - final private String part2; - final private String part3; - final private String part4; - - public DoctypeToken(final String part1, final String part2, final String part3, final String part4) { - this.part1 = part1 != null ? part1.toUpperCase() : part1; - this.part2 = part2 != null ? part2.toUpperCase() : part2; - this.part3 = clean(part3); - this.part4 = clean(part4); - } - - public String getContent() { - String result = ""; - return result; - } - - public String getName() { - return ""; - } - - public String getPart1() { - return part1; - } - - public String getPart2() { - return part2; - } - - public String getPart3() { - return part3; - } - - public String getPart4() { - return part4; - } - - public boolean isValid() { - if (part1 == null || "".equals(part1)) { - return false; - } - - if (!"public".equalsIgnoreCase(part2) && !"system".equalsIgnoreCase(part2)) { - return false; - } - - if ("system".equalsIgnoreCase(part2) && part4 != null && !"".equals(part4)) { - return false; - } - - if ("public".equalsIgnoreCase(part2) && (part4 == null || "".equals(part4))) { - return false; - } - - return true; - } - - public void serialize(final Serializer serializer, final Writer writer) throws IOException { - writer.write(getContent() + "\n"); - } - - public String toString() { - return getContent(); - } -} diff --git a/src/org/htmlcleaner/DomSerializer.java b/src/org/htmlcleaner/DomSerializer.java deleted file mode 100644 index c98e3ba..0000000 --- a/src/org/htmlcleaner/DomSerializer.java +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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.htmlcleaner; - -import org.w3c.dom.Comment; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - *

- * DOM serializer - creates xml DOM. - *

- */ -public class DomSerializer { - protected CleanerProperties props; - protected boolean escapeXml = true; - - public DomSerializer(final CleanerProperties props, final boolean escapeXml) { - this.props = props; - this.escapeXml = escapeXml; - } - - public DomSerializer(final CleanerProperties props) { - this(props, true); - } - - public Document createDOM(final TagNode rootNode) throws ParserConfigurationException { - final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - final Document document = factory.newDocumentBuilder().newDocument(); - final Element rootElement = createElement(rootNode, document); - document.appendChild(rootElement); - setAttributes(rootNode, rootElement); - createSubnodes(document, rootElement, rootNode.getChildren()); - return document; - } - - private Element createElement(final TagNode node, final Document document) { - String name = node.getName(); - final boolean nsAware = props.isNamespacesAware(); - final String prefix = Utils.getXmlNSPrefix(name); - final Map nsDeclarations = node.getNamespaceDeclarations(); - String nsURI = null; - if (prefix != null) { - if (nsAware) { - if (nsDeclarations != null) { - nsURI = nsDeclarations.get(prefix); - } - if (nsURI == null) { - nsURI = node.getNamespaceURIOnPath(prefix); - } - if (nsURI == null) { - nsURI = prefix; - } - } else { - name = Utils.getXmlName(name); - } - } else { - if (nsAware) { - if (nsDeclarations != null) { - nsURI = nsDeclarations.get(""); - } - if (nsURI == null) { - nsURI = node.getNamespaceURIOnPath(prefix); - } - } - } - - if (nsAware && nsURI != null) { - return document.createElementNS(nsURI, name); - } else { - return document.createElement(name); - } - } - - private void setAttributes(final TagNode node, final Element element) { - for (final Map.Entry entry : node.getAttributes().entrySet()) { - final String attrName = entry.getKey(); - String attrValue = entry.getValue(); - if (escapeXml) { - attrValue = Utils.escapeXml(attrValue, props, true); - } - - final String attPrefix = Utils.getXmlNSPrefix(attrName); - if (attPrefix != null) { - if (props.isNamespacesAware()) { - String nsURI = node.getNamespaceURIOnPath(attPrefix); - if (nsURI == null) { - nsURI = attPrefix; - } - element.setAttributeNS(nsURI, attrName, attrValue); - } else { - element.setAttribute(Utils.getXmlName(attrName), attrValue); - } - } else { - element.setAttribute(attrName, attrValue); - } - } - } - - private void createSubnodes(final Document document, final Element element, final List tagChildren) { - if (tagChildren != null) { - final Iterator it = tagChildren.iterator(); - while (it.hasNext()) { - final Object item = it.next(); - if (item instanceof CommentNode) { - final CommentNode commentNode = (CommentNode) item; - final Comment comment = document.createComment(commentNode.getContent().toString()); - element.appendChild(comment); - } else if (item instanceof ContentNode) { - final String nodeName = element.getNodeName(); - String content = item.toString(); - final boolean specialCase = props.isUseCdataForScriptAndStyle() - && ("script".equalsIgnoreCase(nodeName) || "style".equalsIgnoreCase(nodeName)); - if (escapeXml && !specialCase) { - content = Utils.escapeXml(content, props, true); - } - element.appendChild(specialCase ? document.createCDATASection(content) : document - .createTextNode(content)); - } else if (item instanceof TagNode) { - final TagNode subTagNode = (TagNode) item; - final Element subelement = createElement(subTagNode, document); - - setAttributes(subTagNode, subelement); - - // recursively create subnodes - createSubnodes(document, subelement, subTagNode.getChildren()); - - element.appendChild(subelement); - } else if (item instanceof List) { - final List sublist = (List) item; - createSubnodes(document, element, sublist); - } - } - } - } - -} diff --git a/src/org/htmlcleaner/EndTagToken.java b/src/org/htmlcleaner/EndTagToken.java deleted file mode 100644 index 0ead7a4..0000000 --- a/src/org/htmlcleaner/EndTagToken.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.Writer; - -/** - *

- * HTML tag end token. - *

- */ -public class EndTagToken extends TagToken { - public EndTagToken() { - super(); - } - - public EndTagToken(final String name) { - super(name == null ? null : name.toLowerCase()); - } - - public void setAttribute(final String attName, final String attValue) { - // do nothing - simply ignore attributes in closing tag - } - - public void serialize(final Serializer serializer, final Writer writer) { - // do nothing - simply ignore serialization - } - -} diff --git a/src/org/htmlcleaner/FastHtmlSerializer.java b/src/org/htmlcleaner/FastHtmlSerializer.java deleted file mode 100644 index 9f9e715..0000000 --- a/src/org/htmlcleaner/FastHtmlSerializer.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -public class FastHtmlSerializer extends HtmlSerializer { - public FastHtmlSerializer(final CleanerProperties props) { - super(props); - } - - protected void serialize(final TagNode tagNode, final Writer writer) throws IOException { - final Stack tagStack = new Stack(); - final Stack> childStack = new Stack>(); - serializeOpenTag(tagNode, writer, false); - if (!isMinimizedTagSyntax(tagNode)) { - tagStack.push(tagNode); - childStack.push(new ArrayList(tagNode.getChildren())); - while (!tagStack.isEmpty()) { - final TagNode tag = tagStack.peek(); - final List children = childStack.peek(); - if (children.isEmpty()) { - tagStack.pop(); - childStack.pop(); - if (!isMinimizedTagSyntax(tag)) { - serializeEndTag(tag, writer, false); - } - } else { - final Object item = children.get(0); - children.remove(0); - if (item instanceof ContentNode) { - final String content = item.toString(); - writer.write(dontEscape(tag) ? content : escapeText(content)); - } else if (item instanceof TagNode) { - final TagNode currentTag = (TagNode) item; - serializeOpenTag(currentTag, writer, false); - tagStack.push(currentTag); - childStack.push(new ArrayList(currentTag.getChildren())); - } else if (item instanceof BaseToken) { - ((BaseToken) item).serialize(this, writer); - } - } - } - } - } -} diff --git a/src/org/htmlcleaner/HtmlCleaner.java b/src/org/htmlcleaner/HtmlCleaner.java deleted file mode 100644 index 84adae4..0000000 --- a/src/org/htmlcleaner/HtmlCleaner.java +++ /dev/null @@ -1,932 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.*; -import java.net.URL; -import java.net.URLConnection; -import java.util.*; - -/** - * Main HtmlCleaner class. - * - *

- * It represents public interface to the user. It's task is to call tokenizer - * with specified source HTML, traverse list of produced token list and create - * internal object model. It also offers a set of methods to write resulting XML - * to string, file or any output stream. - *

- *

- * Typical usage is the following: - *

- * - * // create an instance of HtmlCleaner HtmlCleaner cleaner = new - * HtmlCleaner(); - * - * // take default cleaner properties CleanerProperties props = - * cleaner.getProperties(); - * - * // customize cleaner's behaviour with property setters props.setXXX(...); - * - * // Clean HTML taken from simple string, file, URL, input stream, // input - * source or reader. Result is root node of created // tree-like structure. - * Single cleaner instance may be safely used // multiple times. TagNode node = - * cleaner.clean(...); - * - * // optionally find parts of the DOM or modify some nodes TagNode[] myNodes = - * node.getElementsByXXX(...); // and/or Object[] myNodes = - * node.evaluateXPath(xPathExpression); // and/or aNode.removeFromTree(); // - * and/or aNode.addAttribute(attName, attValue); // and/or - * aNode.removeAttribute(attName, attValue); // and/or - * cleaner.setInnerHtml(aNode, htmlContent); // and/or do some other tree - * manipulation/traversal - * - * // serialize a node to a file, output stream, DOM, JDom... new - * XXXSerializer(props).writeXmlXXX(aNode, ...); myJDom = new - * JDomSerializer(props, true).createJDom(aNode); myDom = new - * DomSerializer(props, true).createDOM(aNode); - */ -public class HtmlCleaner { - - private class CleanTimeValues { - private OpenTags openTags; - private boolean headOpened = false; - private boolean bodyOpened = false; - private Set headTags = new LinkedHashSet(); - private Set allTags = new TreeSet(); - - private TagNode htmlNode; - private TagNode bodyNode; - private TagNode headNode; - private TagNode rootNode; - - private Set pruneTagSet = new HashSet(); - private Set pruneNodeSet = new HashSet(); - } - - /** - * Class that contains information and mathods for managing list of open, - * but unhandled tags. - */ - private class OpenTags { - private List list = new ArrayList(); - private TagPos last = null; - private Set set = new HashSet(); - - private void addTag(String tagName, int position) { - last = new TagPos(position, tagName); - list.add(last); - set.add(tagName); - } - - private TagPos findFirstTagPos() { - return list.isEmpty() ? null : list.get(0); - } - - private TagPos findTag(String tagName) { - if (tagName != null) { - final ListIterator it = list.listIterator(list.size()); - String fatalTag = null; - final TagInfo fatalInfo = tagInfoProvider.getTagInfo(tagName); - if (fatalInfo != null) { - fatalTag = fatalInfo.getFatalTag(); - } - - while (it.hasPrevious()) { - final TagPos currTagPos = it.previous(); - if (tagName.equals(currTagPos.name)) { - return currTagPos; - } else if (fatalTag != null && fatalTag.equals(currTagPos.name)) { - // do not search past a fatal tag for this tag - return null; - } - } - } - - return null; - } - - private TagPos findTagToPlaceRubbish() { - TagPos result = null, prev = null; - - if (!isEmpty()) { - final ListIterator it = list.listIterator(list.size()); - while (it.hasPrevious()) { - result = it.previous(); - if (result.info == null || result.info.allowsAnything()) { - if (prev != null) { - return prev; - } - } - prev = result; - } - } - - return result; - } - - private TagPos getLastTagPos() { - return last; - } - - private boolean isEmpty() { - return list.isEmpty(); - } - - private void removeTag(final String tagName) { - final ListIterator it = list.listIterator(list.size()); - while (it.hasPrevious()) { - final TagPos currTagPos = it.previous(); - if (tagName.equals(currTagPos.name)) { - it.remove(); - break; - } - } - - last = list.isEmpty() ? null : list.get(list.size() - 1); - } - - /** - * Checks if any of tags specified in the set are already open. - * - * @param tags - */ - private boolean someAlreadyOpen(final Set tags) { - final Iterator it = list.iterator(); - while (it.hasNext()) { - final TagPos curr = it.next(); - if (tags.contains(curr.name)) { - return true; - } - } - return false; - } - - private boolean tagEncountered(final String tagName) { - return set.contains(tagName); - } - - private boolean tagExists(final String tagName) { - return findTag(tagName) != null; - } - } - - /** - * Contains information about single open tag - */ - private class TagPos { - private int position; - private String name; - private TagInfo info; - - TagPos(final int position, final String name) { - this.position = position; - this.name = name; - this.info = tagInfoProvider.getTagInfo(name); - } - } - - public static final String DEFAULT_CHARSET = System.getProperty("file.encoding"); - private CleanerProperties properties; - private ITagInfoProvider tagInfoProvider; - private CleanerTransformations transformations = null; - - /** - * Constructor - creates cleaner instance with default tag info provider and - * default properties. - */ - public HtmlCleaner() { - this(null, null); - } - - /** - * Constructor - creates the instance with default tag info provider and - * specified properties - * - * @param properties - * Properties used during parsing and serializing - */ - public HtmlCleaner(final CleanerProperties properties) { - this(null, properties); - } - - /** - * Constructor - creates the instance with specified tag info provider and - * default properties - * - * @param tagInfoProvider - * Provider for tag filtering and balancing - */ - public HtmlCleaner(final ITagInfoProvider tagInfoProvider) { - this(tagInfoProvider, null); - } - - /** - * Constructor - creates the instance with specified tag info provider and - * specified properties - * - * @param tagInfoProvider - * Provider for tag filtering and balancing - * @param properties - * Properties used during parsing and serializing - */ - public HtmlCleaner(final ITagInfoProvider tagInfoProvider, final CleanerProperties properties) { - this.tagInfoProvider = (tagInfoProvider == null) ? DefaultTagProvider.getInstance() : tagInfoProvider; - this.properties = properties == null ? new CleanerProperties() : properties; - this.properties.setTagInfoProvider(this.tagInfoProvider); - } - - /** - * Add attributes from specified map to the specified tag. If some attribute - * already exist it is preserved. - * - * @param tag - * @param attributes - */ - private void addAttributesToTag(final TagNode tag, final Map attributes) { - if (attributes != null) { - final Map tagAttributes = tag.getAttributes(); - final Iterator it = attributes.entrySet().iterator(); - while (it.hasNext()) { - final Map.Entry currEntry = (Map.Entry) it.next(); - final String attName = (String) currEntry.getKey(); - if (!tagAttributes.containsKey(attName)) { - final String attValue = (String) currEntry.getValue(); - tag.setAttribute(attName, attValue); - } - } - } - } - - /** - * Checks if specified tag with specified info is candidate for moving to - * head section. - * - * @param tagInfo - * @param tagNode - */ - private void addPossibleHeadCandidate(final TagInfo tagInfo, final TagNode tagNode, - final CleanTimeValues cleanTimeValues) { - if (tagInfo != null && tagNode != null) { - if (tagInfo.isHeadTag() - || (tagInfo.isHeadAndBodyTag() && cleanTimeValues.headOpened && !cleanTimeValues.bodyOpened)) { - cleanTimeValues.headTags.add(tagNode); - } - } - } - - /** - * Assigns root node to internal variable. Root node of the result depends - * on parameter "omitHtmlEnvelope". If it is set, then first child of the - * body will be root node, or html will be root node otherwise. - */ - private void calculateRootNode(final CleanTimeValues cleanTimeValues) { - cleanTimeValues.rootNode = cleanTimeValues.htmlNode; - - if (properties.isOmitHtmlEnvelope()) { - final List bodyChildren = cleanTimeValues.bodyNode.getChildren(); - if (bodyChildren != null) { - for (Object child : bodyChildren) { - // if found child that is tag itself, then return it - if (child instanceof TagNode) { - cleanTimeValues.rootNode = (TagNode) child; - break; - } - } - } - } - } - - public TagNode clean(final File file) throws IOException { - return clean(file, DEFAULT_CHARSET); - } - - public TagNode clean(final File file, final String charset) throws IOException { - final FileInputStream in = new FileInputStream(file); - final Reader reader = new InputStreamReader(in, charset); - return clean(reader); - } - - public TagNode clean(final InputStream in) throws IOException { - return clean(in, DEFAULT_CHARSET); - } - - public TagNode clean(final InputStream in, final String charset) throws IOException { - return clean(new InputStreamReader(in, charset)); - } - - public TagNode clean(final Reader reader) throws IOException { - return clean(reader, new CleanTimeValues()); - } - - /** - * Basic version of the cleaning call. - * - * @param reader - * @return An instance of TagNode object which is the root of the XML tree. - * @throws IOException - */ - public TagNode clean(final Reader reader, final CleanTimeValues cleanTimeValues) throws IOException { - cleanTimeValues.openTags = new OpenTags(); - cleanTimeValues.headOpened = false; - cleanTimeValues.bodyOpened = false; - cleanTimeValues.headTags.clear(); - cleanTimeValues.allTags.clear(); - setPruneTags(properties.getPruneTags(), cleanTimeValues); - - cleanTimeValues.htmlNode = createTagNode("html", cleanTimeValues); - cleanTimeValues.bodyNode = createTagNode("body", cleanTimeValues); - cleanTimeValues.headNode = createTagNode("head", cleanTimeValues); - cleanTimeValues.rootNode = null; - cleanTimeValues.htmlNode.addChild(cleanTimeValues.headNode); - cleanTimeValues.htmlNode.addChild(cleanTimeValues.bodyNode); - - final HtmlTokenizer htmlTokenizer = new HtmlTokenizer(reader, properties, transformations, tagInfoProvider) { - @Override - public TagNode createTagNode(final String name) { - return HtmlCleaner.this.createTagNode(name, cleanTimeValues); - } - - @Override - public void makeTree(final List tokenList) { - HtmlCleaner.this.makeTree(tokenList, tokenList.listIterator(tokenList.size() - 1), cleanTimeValues); - } - }; - - htmlTokenizer.start(); - - final List nodeList = htmlTokenizer.getTokenList(); - closeAll(nodeList, cleanTimeValues); - createDocumentNodes(nodeList, cleanTimeValues); - - calculateRootNode(cleanTimeValues); - - // if there are some nodes to prune from tree - if (cleanTimeValues.pruneNodeSet != null && !cleanTimeValues.pruneNodeSet.isEmpty()) { - final Iterator iterator = cleanTimeValues.pruneNodeSet.iterator(); - while (iterator.hasNext()) { - final TagNode tagNode = (TagNode) iterator.next(); - final TagNode parent = tagNode.getParent(); - if (parent != null) { - parent.removeChild(tagNode); - } - } - } - - cleanTimeValues.rootNode.setDocType(htmlTokenizer.getDocType()); - - return cleanTimeValues.rootNode; - } - - public TagNode clean(final String htmlContent) { - try { - return clean(new StringReader(htmlContent)); - } catch (final IOException e) { - // should never happen because reading from StringReader - throw new HtmlCleanerException(e); - } - } - - /** - * Creates instance from the content downloaded from specified URL. HTML - * encoding is resolved following the attempts in the sequence: 1. reading - * Content-Type response header, 2. Analyzing META tags at the beginning of - * the html, 3. Using platform's default charset. - * - * @param url - * @return - * @throws IOException - */ - public TagNode clean(final URL url) throws IOException { - return clean(url, null); - } - - public TagNode clean(final URL url, String charset) throws IOException { - final URLConnection urlConnection = url.openConnection(); - if (charset == null) { - charset = Utils.getCharsetFromContentTypeString(urlConnection.getHeaderField("Content-Type")); - } - if (charset == null) { - charset = Utils.getCharsetFromContent(url); - } - if (charset == null) { - charset = DEFAULT_CHARSET; - } - return clean(url.openStream(), charset); - } - - /** - * Close all unclosed tags if there are any. - */ - private void closeAll(final List nodeList, final CleanTimeValues cleanTimeValues) { - final TagPos firstTagPos = cleanTimeValues.openTags.findFirstTagPos(); - if (firstTagPos != null) { - closeSnippet(nodeList, firstTagPos, null, cleanTimeValues); - } - } - - private List closeSnippet(final List nodeList, final TagPos tagPos, final Object toNode, - final CleanTimeValues cleanTimeValues) { - final List closed = new ArrayList(); - final ListIterator it = nodeList.listIterator(tagPos.position); - - TagNode tagNode = null; - Object item = it.next(); - boolean isListEnd = false; - - while ((toNode == null && !isListEnd) || (toNode != null && item != toNode)) { - if (isStartToken(item)) { - final TagNode startTagToken = (TagNode) item; - closed.add(startTagToken); - final List itemsToMove = startTagToken.getItemsToMove(); - if (itemsToMove != null) { - final OpenTags prevOpenTags = cleanTimeValues.openTags; - cleanTimeValues.openTags = new OpenTags(); - makeTree(itemsToMove, itemsToMove.listIterator(0), cleanTimeValues); - closeAll(itemsToMove, cleanTimeValues); - startTagToken.setItemsToMove(null); - cleanTimeValues.openTags = prevOpenTags; - } - - final TagNode newTagNode = createTagNode(startTagToken); - final TagInfo tag = tagInfoProvider.getTagInfo(newTagNode.getName()); - addPossibleHeadCandidate(tag, newTagNode, cleanTimeValues); - if (tagNode != null) { - tagNode.addChildren(itemsToMove); - tagNode.addChild(newTagNode); - it.set(null); - } else { - if (itemsToMove != null) { - itemsToMove.add(newTagNode); - it.set(itemsToMove); - } else { - it.set(newTagNode); - } - } - - cleanTimeValues.openTags.removeTag(newTagNode.getName()); - tagNode = newTagNode; - } else { - if (tagNode != null) { - it.set(null); - if (item != null) { - tagNode.addChild(item); - } - } - } - - if (it.hasNext()) { - item = it.next(); - } else { - isListEnd = true; - } - } - - return closed; - } - - private void createDocumentNodes(final List listNodes, final CleanTimeValues cleanTimeValues) { - final Iterator it = listNodes.iterator(); - while (it.hasNext()) { - final Object child = it.next(); - - if (child == null) { - continue; - } - - boolean toAdd = true; - - if (child instanceof TagNode) { - final TagNode node = (TagNode) child; - final TagInfo tag = tagInfoProvider.getTagInfo(node.getName()); - addPossibleHeadCandidate(tag, node, cleanTimeValues); - } else { - if (child instanceof ContentNode) { - toAdd = !"".equals(child.toString()); - } - } - - if (toAdd) { - cleanTimeValues.bodyNode.addChild(child); - } - } - - // move all viable head candidates to head section of the tree - final Iterator headIterator = cleanTimeValues.headTags.iterator(); - while (headIterator.hasNext()) { - final TagNode headCandidateNode = (TagNode) headIterator.next(); - - // check if this node is already inside a candidate for moving to - // head - TagNode parent = headCandidateNode.getParent(); - boolean toMove = true; - while (parent != null) { - if (cleanTimeValues.headTags.contains(parent)) { - toMove = false; - break; - } - parent = parent.getParent(); - } - - if (toMove) { - headCandidateNode.removeFromTree(); - cleanTimeValues.headNode.addChild(headCandidateNode); - } - } - } - - private TagNode createTagNode(final String name, final CleanTimeValues cleanTimeValues) { - final TagNode node = new TagNode(name); - if (cleanTimeValues.pruneTagSet != null && name != null - && cleanTimeValues.pruneTagSet.contains(name.toLowerCase())) { - cleanTimeValues.pruneNodeSet.add(node); - } - return node; - } - - private TagNode createTagNode(final TagNode startTagToken) { - startTagToken.setFormed(); - return startTagToken; - } - - /** - * For the specified node, returns it's content as string. - * - * @param node - */ - public String getInnerHtml(final TagNode node) { - if (node != null) { - try { - final String content = new SimpleXmlSerializer(properties).getAsString(node); - int index1 = content.indexOf("<" + node.getName()); - index1 = content.indexOf('>', index1 + 1); - final int index2 = content.lastIndexOf('<'); - return index1 >= 0 && index1 <= index2 ? content.substring(index1 + 1, index2) : null; - } catch (final IOException e) { - throw new HtmlCleanerException(e); - } - } else { - throw new HtmlCleanerException("Cannot return inner html of the null node!"); - } - } - - public CleanerProperties getProperties() { - return properties; - } - - /** - * @return ITagInfoProvider instance for this HtmlCleaner - */ - public ITagInfoProvider getTagInfoProvider() { - return tagInfoProvider; - } - - /** - * @return Transormations defined for this instance of cleaner - */ - public CleanerTransformations getTransformations() { - return transformations; - } - - private boolean isAllowedInLastOpenTag(final BaseToken token, final CleanTimeValues cleanTimeValues) { - final TagPos last = cleanTimeValues.openTags.getLastTagPos(); - if (last != null) { - if (last.info != null) { - return last.info.allowsItem(token); - } - } - - return true; - } - - /** - * Checks if open fatal tag is missing if there is a fatal tag for the - * specified tag. - * - * @param tag - */ - private boolean isFatalTagSatisfied(final TagInfo tag, final CleanTimeValues cleanTimeValues) { - if (tag != null) { - final String fatalTagName = tag.getFatalTag(); - return fatalTagName == null ? true : cleanTimeValues.openTags.tagExists(fatalTagName); - } - - return true; - } - - private boolean isStartToken(final Object o) { - return (o instanceof TagNode) && !((TagNode) o).isFormed(); - } - - private TagNode makeTagNodeCopy(final TagNode tagNode, final CleanTimeValues cleanTimeValues) { - final TagNode copy = tagNode.makeCopy(); - if (cleanTimeValues.pruneTagSet != null && cleanTimeValues.pruneTagSet.contains(tagNode.getName())) { - cleanTimeValues.pruneNodeSet.add(copy); - } - return copy; - } - - public void makeTree(final List nodeList, final ListIterator nodeIterator, - final CleanTimeValues cleanTimeValues) { - // process while not reach the end of the list - while (nodeIterator.hasNext()) { - final BaseToken token = nodeIterator.next(); - - if (token instanceof EndTagToken) { - final EndTagToken endTagToken = (EndTagToken) token; - final String tagName = endTagToken.getName(); - final TagInfo tag = tagInfoProvider.getTagInfo(tagName); - - if ((tag == null && properties.isOmitUnknownTags()) - || (tag != null && tag.isDeprecated() && properties.isOmitDeprecatedTags())) { - nodeIterator.set(null); - } else if (tag != null && !tag.allowsBody()) { - nodeIterator.set(null); - } else { - final TagPos matchingPosition = cleanTimeValues.openTags.findTag(tagName); - - if (matchingPosition != null) { - final List closed = closeSnippet(nodeList, matchingPosition, endTagToken, cleanTimeValues); - nodeIterator.set(null); - for (int i = closed.size() - 1; i >= 1; i--) { - final TagNode closedTag = (TagNode) closed.get(i); - if (tag != null && tag.isContinueAfter(closedTag.getName())) { - nodeIterator.add(makeTagNodeCopy(closedTag, cleanTimeValues)); - nodeIterator.previous(); - } - } - } else if (!isAllowedInLastOpenTag(token, cleanTimeValues)) { - saveToLastOpenTag(nodeList, token, cleanTimeValues); - nodeIterator.set(null); - } - } - } else if (isStartToken(token)) { - final TagNode startTagToken = (TagNode) token; - final String tagName = startTagToken.getName(); - final TagInfo tag = tagInfoProvider.getTagInfo(tagName); - - final TagPos lastTagPos = cleanTimeValues.openTags.isEmpty() ? null : cleanTimeValues.openTags - .getLastTagPos(); - final TagInfo lastTagInfo = lastTagPos == null ? null : tagInfoProvider.getTagInfo(lastTagPos.name); - - // add tag to set of all tags - cleanTimeValues.allTags.add(tagName); - - // HTML open tag - if ("html".equals(tagName)) { - addAttributesToTag(cleanTimeValues.htmlNode, startTagToken.getAttributes()); - nodeIterator.set(null); - // BODY open tag - } else if ("body".equals(tagName)) { - cleanTimeValues.bodyOpened = true; - addAttributesToTag(cleanTimeValues.bodyNode, startTagToken.getAttributes()); - nodeIterator.set(null); - // HEAD open tag - } else if ("head".equals(tagName)) { - cleanTimeValues.headOpened = true; - addAttributesToTag(cleanTimeValues.headNode, startTagToken.getAttributes()); - nodeIterator.set(null); - // unknown HTML tag and unknown tags are not allowed - } else if ((tag == null && properties.isOmitUnknownTags()) - || (tag != null && tag.isDeprecated() && properties.isOmitDeprecatedTags())) { - nodeIterator.set(null); - // if current tag is unknown, unknown tags are allowed and - // last open tag doesn't allow any other tags in its body - } else if (tag == null && lastTagInfo != null && !lastTagInfo.allowsAnything()) { - saveToLastOpenTag(nodeList, token, cleanTimeValues); - nodeIterator.set(null); - } else if (tag != null && tag.hasPermittedTags() - && cleanTimeValues.openTags.someAlreadyOpen(tag.getPermittedTags())) { - nodeIterator.set(null); - // if tag that must be unique, ignore this occurence - } else if (tag != null && tag.isUnique() && cleanTimeValues.openTags.tagEncountered(tagName)) { - nodeIterator.set(null); - // if there is no required outer tag without that this open - // tag is ignored - } else if (!isFatalTagSatisfied(tag, cleanTimeValues)) { - nodeIterator.set(null); - // if there is no required parent tag - it must be added - // before this open tag - } else if (mustAddRequiredParent(tag, cleanTimeValues)) { - final String requiredParent = tag.getRequiredParent(); - final TagNode requiredParentStartToken = createTagNode(requiredParent, cleanTimeValues); - nodeIterator.previous(); - nodeIterator.add(requiredParentStartToken); - nodeIterator.previous(); - // if last open tag has lower presidence then this, it must - // be closed - } else if (tag != null && lastTagPos != null && tag.isMustCloseTag(lastTagInfo)) { - final List closed = closeSnippet(nodeList, lastTagPos, startTagToken, cleanTimeValues); - final int closedCount = closed.size(); - - // it is needed to copy some tags again in front of current, - // if there are any - if (tag.hasCopyTags() && closedCount > 0) { - // first iterates over list from the back and collects - // all start tokens - // in sequence that must be copied - final ListIterator closedIt = closed.listIterator(closedCount); - final List toBeCopied = new ArrayList(); - while (closedIt.hasPrevious()) { - final TagNode currStartToken = (TagNode) closedIt.previous(); - if (tag.isCopy(currStartToken.getName())) { - toBeCopied.add(0, currStartToken); - } else { - break; - } - } - - if (toBeCopied.size() > 0) { - final Iterator copyIt = toBeCopied.iterator(); - while (copyIt.hasNext()) { - final TagNode currStartToken = (TagNode) copyIt.next(); - nodeIterator.add(makeTagNodeCopy(currStartToken, cleanTimeValues)); - } - - // back to the previous place, before adding new - // start tokens - for (int i = 0; i < toBeCopied.size(); i++) { - nodeIterator.previous(); - } - } - } - - nodeIterator.previous(); - // if this open tag is not allowed inside last open tag, - // then it must be moved to the place where it can be - } else if (!isAllowedInLastOpenTag(token, cleanTimeValues)) { - saveToLastOpenTag(nodeList, token, cleanTimeValues); - nodeIterator.set(null); - // if it is known HTML tag but doesn't allow body, it is - // immediately closed - } else if (tag != null && !tag.allowsBody()) { - final TagNode newTagNode = createTagNode(startTagToken); - addPossibleHeadCandidate(tag, newTagNode, cleanTimeValues); - nodeIterator.set(newTagNode); - // default case - just remember this open tag and go further - } else { - cleanTimeValues.openTags.addTag(tagName, nodeIterator.previousIndex()); - } - } else { - if (!isAllowedInLastOpenTag(token, cleanTimeValues)) { - saveToLastOpenTag(nodeList, token, cleanTimeValues); - nodeIterator.set(null); - } - } - } - } - - /** - * Check if specified tag requires parent tag, but that parent tag is - * missing in the appropriate context. - * - * @param tag - */ - private boolean mustAddRequiredParent(final TagInfo tag, final CleanTimeValues cleanTimeValues) { - if (tag != null) { - final String requiredParent = tag.getRequiredParent(); - if (requiredParent != null) { - final String fatalTag = tag.getFatalTag(); - int fatalTagPositon = -1; - if (fatalTag != null) { - final TagPos tagPos = cleanTimeValues.openTags.findTag(fatalTag); - if (tagPos != null) { - fatalTagPositon = tagPos.position; - } - } - - // iterates through the list of open tags from the end and check - // if there is some higher - final ListIterator it = cleanTimeValues.openTags.list - .listIterator(cleanTimeValues.openTags.list.size()); - while (it.hasPrevious()) { - final TagPos currTagPos = it.previous(); - if (tag.isHigher(currTagPos.name)) { - return currTagPos.position <= fatalTagPositon; - } - } - - return true; - } - } - - return false; - } - - private void saveToLastOpenTag(final List nodeList, final BaseToken tokenToAdd, - final CleanTimeValues cleanTimeValues) { - final TagPos last = cleanTimeValues.openTags.getLastTagPos(); - if (last != null && last.info != null && last.info.isIgnorePermitted()) { - return; - } - - final TagPos rubbishPos = cleanTimeValues.openTags.findTagToPlaceRubbish(); - if (rubbishPos != null) { - final TagNode startTagToken = (TagNode) nodeList.get(rubbishPos.position); - startTagToken.addItemForMoving(tokenToAdd); - } - } - - /** - * For the specified tag node, defines it's html content. This causes - * cleaner to reclean given html portion and insert it inside the node - * instead of previous content. - * - * @param node - * @param content - */ - public void setInnerHtml(final TagNode node, final String content) { - if (node != null) { - final String nodeName = node.getName(); - final StringBuilder html = new StringBuilder(); - html.append("<" + nodeName + " marker=''>"); - html.append(content); - html.append(""); - TagNode parent = node.getParent(); - while (parent != null) { - final String parentName = parent.getName(); - html.insert(0, "<" + parentName + ">"); - html.append(""); - parent = parent.getParent(); - } - - final TagNode rootNode = clean(html.toString()); - final TagNode cleanedNode = rootNode.findElementHavingAttribute("marker", true); - if (cleanedNode != null) { - node.setChildren(cleanedNode.getChildren()); - } - } - } - - private void setPruneTags(final String pruneTags, final CleanTimeValues cleanTimeValues) { - cleanTimeValues.pruneTagSet.clear(); - cleanTimeValues.pruneNodeSet.clear(); - if (pruneTags != null) { - final StringTokenizer tokenizer = new StringTokenizer(pruneTags, ","); - while (tokenizer.hasMoreTokens()) { - cleanTimeValues.pruneTagSet.add(tokenizer.nextToken().trim().toLowerCase()); - } - } - } - - /** - * Sets tranformations for this cleaner instance. - * - * @param transformations - */ - public void setTransformations(final CleanerTransformations transformations) { - this.transformations = transformations; - } - -} diff --git a/src/org/htmlcleaner/HtmlCleanerException.java b/src/org/htmlcleaner/HtmlCleanerException.java deleted file mode 100644 index 2e1b099..0000000 --- a/src/org/htmlcleaner/HtmlCleanerException.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -/** - *

- * General HtmlCleaner runtime exception. - *

- */ -public class HtmlCleanerException extends RuntimeException { - private static final long serialVersionUID = 1L; - - public HtmlCleanerException() { - this("HtmlCleaner expression occureed!"); - } - - public HtmlCleanerException(final String message) { - super(message); - } - - public HtmlCleanerException(final String message, final Throwable cause) { - super(message, cause); - } - - public HtmlCleanerException(final Throwable cause) { - super(cause); - } -} diff --git a/src/org/htmlcleaner/HtmlNode.java b/src/org/htmlcleaner/HtmlNode.java deleted file mode 100644 index 6aca683..0000000 --- a/src/org/htmlcleaner/HtmlNode.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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.htmlcleaner; - -/** - * Marker interface denoting nodes of the document tree - */ -public interface HtmlNode { -} diff --git a/src/org/htmlcleaner/HtmlSerializer.java b/src/org/htmlcleaner/HtmlSerializer.java deleted file mode 100644 index a196009..0000000 --- a/src/org/htmlcleaner/HtmlSerializer.java +++ /dev/null @@ -1,237 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.IOException; -import java.io.Writer; -import java.util.Map; - -/** - *

- * Abstract HTML serializer - contains common logic for descendants. - *

- */ -public abstract class HtmlSerializer extends Serializer { - protected HtmlSerializer(final CleanerProperties props) { - super(props); - } - - protected boolean isMinimizedTagSyntax(final TagNode tagNode) { - final TagInfo tagInfo = props.getTagInfoProvider().getTagInfo(tagNode.getName()); - return tagInfo != null && !tagNode.hasChildren() && tagInfo.isEmptyTag(); - } - - protected boolean dontEscape(TagNode tagNode) { - return isScriptOrStyle(tagNode); - } - - protected String escapeText(String s) { - final boolean recognizeUnicodeChars = props.isRecognizeUnicodeChars(); - final boolean translateSpecialEntities = props.isTranslateSpecialEntities(); - - if (s != null) { - final int len = s.length(); - final StringBuilder result = new StringBuilder(len); - - for (int i = 0; i < len; i++) { - char ch = s.charAt(i); - - if (ch == '&') { - if (i < len - 2 && s.charAt(i + 1) == '#') { - boolean isHex = Character.toLowerCase(s.charAt(i + 2)) == 'x'; - int charIndex = i + (isHex ? 3 : 2); - int radix = isHex ? 16 : 10; - String unicode = ""; - while (charIndex < len) { - char currCh = s.charAt(charIndex); - if (currCh == ';') { - break; - } else if (Utils.isValidInt(unicode + currCh, radix)) { - unicode += currCh; - charIndex++; - } else { - charIndex--; - break; - } - } - - if (Utils.isValidInt(unicode, radix)) { - char unicodeChar = (char) Integer.parseInt(unicode, radix); - if (!Utils.isValidXmlChar(unicodeChar)) { - i = charIndex; - } else if (!Utils.isReservedXmlChar(unicodeChar)) { - result.append(recognizeUnicodeChars ? String.valueOf(unicodeChar) : "&#" + unicode - + ";"); - i = charIndex; - } else { - i = charIndex; - result.append("&#" + unicode + ";"); - } - } else { - result.append(props.isTransResCharsToNCR() ? "&#" + (int) '&' + ";" : "&"); - } - } else { - // get minimal following sequence required to recognize - // some special entitiy - String seq = s.substring(i, i + Math.min(SpecialEntity.getMaxEntityLength() + 2, len - i)); - int semiIndex = seq.indexOf(';'); - if (semiIndex > 0) { - String entityKey = seq.substring(1, semiIndex); - SpecialEntity entity = SpecialEntity.getEntity(entityKey); - if (entity != null) { - if (translateSpecialEntities) { - result.append(props.isTransSpecialEntitiesToNCR() ? entity.getDecimalNCR() : entity - .getCharacter()); - } else { - result.append(entity.getEscapedValue()); - } - - i += entityKey.length() + 1; - continue; - } - } - - String sub = s.substring(i); - boolean isReservedSeq = false; - for (int j = 0; j < Utils.RESERVED_XML_CHARS_LIST.length; j++) { - final char currentChar = Utils.RESERVED_XML_CHARS_LIST[j]; - seq = Utils.RESERVED_XML_CHARS[currentChar]; - if (sub.startsWith(seq)) { - result.append(props.isTransResCharsToNCR() ? "&#" + (int) currentChar + ";" : seq); - i += seq.length() - 1; - isReservedSeq = true; - break; - } - } - if (!isReservedSeq) { - result.append(props.isTransResCharsToNCR() ? "&#" + (int) '&' + ";" : "&"); - } - } - } else if (Utils.isReservedXmlChar(ch)) { - result.append(props.isTransResCharsToNCR() ? "&#" + (int) ch + ";" : ch); - } else { - result.append(ch); - } - } - - return result.toString(); - } - - return null; - } - - protected void serializeOpenTag(TagNode tagNode, Writer writer, boolean newLine) throws IOException { - String tagName = tagNode.getName(); - - if (Utils.isEmptyString(tagName)) { - return; - } - - boolean nsAware = props.isNamespacesAware(); - - if (!nsAware && Utils.getXmlNSPrefix(tagName) != null) { - tagName = Utils.getXmlName(tagName); - } - - writer.write("<" + tagName); - for (Map.Entry entry : tagNode.getAttributes().entrySet()) { - String attName = entry.getKey(); - if (!nsAware && Utils.getXmlNSPrefix(attName) != null) { - attName = Utils.getXmlName(attName); - } - writer.write(" " + attName + "=\"" + escapeText(entry.getValue()) + "\""); - } - - if (nsAware) { - final Map nsDeclarations = tagNode.getNamespaceDeclarations(); - if (nsDeclarations != null) { - for (Map.Entry entry : nsDeclarations.entrySet()) { - String prefix = entry.getKey(); - String att = "xmlns"; - if (prefix.length() > 0) { - att += ":" + prefix; - } - writer.write(" " + att + "=\"" + escapeText(entry.getValue()) + "\""); - } - } - } - - if (isMinimizedTagSyntax(tagNode)) { - writer.write(" />"); - if (newLine) { - writer.write("\n"); - } - } else { - writer.write(">"); - } - } - - protected void serializeEndTag(TagNode tagNode, Writer writer, boolean newLine) throws IOException { - String tagName = tagNode.getName(); - - if (Utils.isEmptyString(tagName)) { - return; - } - - if (Utils.getXmlNSPrefix(tagName) != null && !props.isNamespacesAware()) { - tagName = Utils.getXmlName(tagName); - } - - writer.write(""); - if (newLine) { - writer.write("\n"); - } - } - -} diff --git a/src/org/htmlcleaner/HtmlTokenizer.java b/src/org/htmlcleaner/HtmlTokenizer.java deleted file mode 100644 index 08a186c..0000000 --- a/src/org/htmlcleaner/HtmlTokenizer.java +++ /dev/null @@ -1,836 +0,0 @@ -/******************************************************************************* - * Copyright 2011 Zheng Sun - * - * Licensed 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. - ******************************************************************************/ - -/* Copyright (c) 2006-2007, Vladimir Nikic - All rights reserved. - - Redistribution and use of this software in source and binary forms, - with or without modification, are permitted provided that the following - conditions are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - - * The name of HtmlCleaner may not be used to endorse or promote - products derived from this software without specific prior - written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - You can contact Vladimir Nikic by sending e-mail to - nikic_vladimir@yahoo.com. Please include the word "HtmlCleaner" in the - subject line. - */ - -package org.htmlcleaner; - -import java.io.*; -import java.util.*; - -/** - * Main HTML tokenizer. - *

- * It's task is to parse HTML and produce list of valid tokens: open tag tokens, - * end tag tokens, contents (text) and comments. As soon as new item is added to - * token list, cleaner is invoked to clean current list at the end. - *

- */ -abstract public class HtmlTokenizer { - private final static int WORKING_BUFFER_SIZE = 1024; - - final private BufferedReader reader; - private char[] working = new char[WORKING_BUFFER_SIZE]; - private transient int pos = 0; - private transient int len = -1; - private transient char saved[] = new char[512]; - private transient int savedLen = 0; - private transient DoctypeToken docType = null; - private transient TagToken currentTagToken = null; - private transient List tokenList = new ArrayList(); - private boolean asExpected = true; - private boolean isScriptContext = false; - private CleanerProperties props; - private boolean isOmitUnknownTags; - final private boolean isTreatUnknownTagsAsContent; - private boolean isOmitDeprecatedTags; - final private boolean isTreatDeprecatedTagsAsContent; - final private boolean isNamespacesAware; - private boolean isOmitComments; - final private boolean isAllowMultiWordAttributes; - private boolean isAllowHtmlInsideAttributes; - private CleanerTransformations transformations; - private ITagInfoProvider tagInfoProvider; - private StringBuilder commonStr = new StringBuilder(); - - /** - * Constructor - cretes instance of the parser with specified content. - * - * @param reader - * @param props - * @param transformations - * @param tagInfoProvider - * - * @throws IOException - */ - public HtmlTokenizer(final Reader reader, final CleanerProperties props, - final CleanerTransformations transformations, final ITagInfoProvider tagInfoProvider) throws IOException { - this.reader = new BufferedReader(reader); - this.props = props; - this.isOmitUnknownTags = props.isOmitUnknownTags(); - this.isTreatUnknownTagsAsContent = props.isTreatUnknownTagsAsContent(); - this.isOmitDeprecatedTags = props.isOmitDeprecatedTags(); - this.isTreatDeprecatedTagsAsContent = props.isTreatDeprecatedTagsAsContent(); - this.isNamespacesAware = props.isNamespacesAware(); - this.isOmitComments = props.isOmitComments(); - this.isAllowMultiWordAttributes = props.isAllowMultiWordAttributes(); - this.isAllowHtmlInsideAttributes = props.isAllowHtmlInsideAttributes(); - this.transformations = transformations; - this.tagInfoProvider = tagInfoProvider; - } - - private boolean addSavedAsContent() { - if (savedLen > 0) { - addToken(new ContentNode(saved, savedLen)); - savedLen = 0; - return true; - } - - return false; - } - - private void addToken(final BaseToken token) { - tokenList.add(token); - makeTree(tokenList); - } - - /** - * Parses a single tag attribute - it is expected to be in one of the forms: - * name=value name="value" name='value' name - * - * @throws IOException - */ - private String attributeValue() throws IOException { - skipWhitespaces(); - - if (isCharSimple('<') || isCharSimple('>') || startsWithSimple("/>")) { - return ""; - } - - boolean isQuoteMode = false; - boolean isAposMode = false; - - commonStr.delete(0, commonStr.length()); - if (isCharSimple('\'')) { - isAposMode = true; - saveCurrentSafe(); - go(); - } else if (isCharSimple('\"')) { - isQuoteMode = true; - saveCurrentSafe(); - go(); - } - - while (!isAllRead() - && (((isAposMode && !isCharEquals('\'') || isQuoteMode && !isCharEquals('\"')) - && (isAllowHtmlInsideAttributes || !isCharEquals('>') && !isCharEquals('<')) && (isAllowMultiWordAttributes || !isWhitespaceSafe())) || (!isAposMode - && !isQuoteMode && !isWhitespaceSafe() && !isCharEquals('>') && !isCharEquals('<')))) { - if (isValidXmlCharSafe()) { - commonStr.append(working[pos]); - saveCurrentSafe(); - } - go(); - } - - if (isCharSimple('\'') && isAposMode) { - saveCurrentSafe(); - go(); - } else if (isCharSimple('\"') && isQuoteMode) { - saveCurrentSafe(); - go(); - } - - return commonStr.toString(); - } - - private void comment() throws IOException { - go(4); - while (!isAllRead() && !startsWithSimple("-->")) { - if (isValidXmlCharSafe()) { - saveCurrentSafe(); - } - go(); - } - - if (startsWithSimple("-->")) { - go(3); - } - - if (savedLen > 0) { - if (!isOmitComments) { - final String hyphenRepl = props.getHyphenReplacementInComment(); - String comment = new String(saved, 0, savedLen).replaceAll("--", hyphenRepl + hyphenRepl); - - if (comment.length() > 0 && comment.charAt(0) == '-') { - comment = hyphenRepl + comment.substring(1); - } - final int len = comment.length(); - if (len > 0 && comment.charAt(len - 1) == '-') { - comment = comment.substring(0, len - 1) + hyphenRepl; - } - - addToken(new CommentNode(comment)); - } - savedLen = 0; - } - } - - private boolean content() throws IOException { - while (!isAllRead()) { - if (isValidXmlCharSafe()) { - saveCurrentSafe(); - } - go(); - - if (isCharSimple('<')) { - break; - } - } - - return addSavedAsContent(); - } - - public abstract TagNode createTagNode(String name); - - private void doctype() throws IOException { - go(9); - - skipWhitespaces(); - final String part1 = identifier(); - skipWhitespaces(); - final String part2 = identifier(); - skipWhitespaces(); - final String part3 = attributeValue(); - skipWhitespaces(); - final String part4 = attributeValue(); - - ignoreUntil('<'); - - docType = new DoctypeToken(part1, part2, part3, part4); - } - - /** - * @return Current character to be read, but first it must be checked if it - * exists. This method is made for performance reasons to be used - * instead of isChar(...). - */ - @SuppressWarnings("unused") - private char getCurrentChar() { - return working[pos]; - } - - public DoctypeToken getDocType() { - return docType; - } - - public List getTokenList() { - return this.tokenList; - } - - private void go() throws IOException { - pos++; - readIfNeeded(0); - } - - private void go(final int step) throws IOException { - pos += step; - readIfNeeded(step - 1); - } - - /** - * Parses an identifier from the current position. - * - * @throws IOException - */ - private String identifier() throws IOException { - asExpected = true; - - if (!isIdentifierStartChar()) { - asExpected = false; - return null; - } - - commonStr.delete(0, commonStr.length()); - - while (!isAllRead() && isIdentifierChar()) { - saveCurrentSafe(); - commonStr.append(working[pos]); - go(); - } - - // strip invalid characters from the end - while (commonStr.length() > 0 && Utils.isIdentifierHelperChar(commonStr.charAt(commonStr.length() - 1))) { - commonStr.deleteCharAt(commonStr.length() - 1); - } - - if (commonStr.length() == 0) { - return null; - } - - String id = commonStr.toString(); - - final int columnIndex = id.indexOf(':'); - if (columnIndex >= 0) { - final String prefix = id.substring(0, columnIndex); - String suffix = id.substring(columnIndex + 1); - final int nextColumnIndex = suffix.indexOf(':'); - if (nextColumnIndex >= 0) { - suffix = suffix.substring(0, nextColumnIndex); - } - id = isNamespacesAware ? (prefix + ":" + suffix) : suffix; - } - - return id; - } - - private void ignoreUntil(final char ch) throws IOException { - while (!isAllRead()) { - go(); - if (isChar(ch)) { - break; - } - } - } - - /** - * Checks if end of the content is reached. - */ - private boolean isAllRead() { - return len >= 0 && pos >= len; - } - - /** - * Checks if character at current runtime position is equal to specified - * char. - * - * @param ch - * @return true is equal, false otherwise. - */ - private boolean isChar(final char ch) { - return isChar(pos, ch); - } - - /** - * Checks if character at specified position is equal to specified char. - * - * @param position - * @param ch - * @return true is equals, false otherwise. - */ - private boolean isChar(final int position, final char ch) { - if (len >= 0 && position >= len) { - return false; - } - - return Character.toLowerCase(ch) == Character.toLowerCase(working[position]); - } - - private boolean isCharEquals(final char ch) { - return working[pos] == ch; - } - - private boolean isCharSimple(final char ch) { - return (len < 0 || pos < len) && (ch == working[pos]); - } - - /** - * Checks if character at current runtime position can be identifier part. - * - * @return true is may be identifier part, false otherwise. - */ - private boolean isIdentifierChar() { - if (len >= 0 && pos >= len) { - return false; - } - - final char ch = working[pos]; - return Character.isUnicodeIdentifierStart(ch) || Character.isDigit(ch) || Utils.isIdentifierHelperChar(ch); - } - - /** - * Checks if character at current runtime position can be identifier start. - * - * @return true is may be identifier start, false otherwise. - */ - private boolean isIdentifierStartChar() { - return isIdentifierStartChar(pos); - } - - /** - * Checks if character at specified position can be identifier start. - * - * @param position - * @return true is may be identifier start, false otherwise. - */ - private boolean isIdentifierStartChar(final int position) { - if (len >= 0 && position >= len) { - return false; - } - - final char ch = working[position]; - return Character.isUnicodeIdentifierStart(ch) || ch == '_'; - } - - /** - * Checks if specified tag name is one of the reserved tags: HTML, HEAD or - * BODY - * - * @param tagName - * @return - */ - private boolean isReservedTag(String tagName) { - tagName = tagName.toLowerCase(); - return "html".equals(tagName) || "head".equals(tagName) || "body".equals(tagName); - } - - private boolean isValidXmlChar() { - return isAllRead() || Utils.isValidXmlChar(working[pos]); - } - - private boolean isValidXmlCharSafe() { - return Utils.isValidXmlChar(working[pos]); - } - - /** - * Checks if character at current runtime position is whitespace. - * - * @return true is whitespace, false otherwise. - */ - @SuppressWarnings("unused") - private boolean isWhitespace() { - return isWhitespace(pos); - } - - /** - * Checks if character at specified position is whitespace. - * - * @param position - * @return true is whitespace, false otherwise. - */ - private boolean isWhitespace(final int position) { - if (len >= 0 && position >= len) { - return false; - } - - return Character.isWhitespace(working[position]); - } - - private boolean isWhitespaceSafe() { - return Character.isWhitespace(working[pos]); - } - - public abstract void makeTree(List tokenList); - - private void readIfNeeded(final int neededChars) throws IOException { - if (len == -1 && pos + neededChars >= WORKING_BUFFER_SIZE) { - final int numToCopy = WORKING_BUFFER_SIZE - pos; - System.arraycopy(working, pos, working, 0, numToCopy); - pos = 0; - - int expected = WORKING_BUFFER_SIZE - numToCopy; - int size = 0; - int charsRead; - int offset = numToCopy; - do { - charsRead = reader.read(working, offset, expected); - if (charsRead >= 0) { - size += charsRead; - offset += charsRead; - expected -= charsRead; - } - } while (charsRead >= 0 && expected > 0); - - if (expected > 0) { - len = size + numToCopy; - } - - // convert invalid XML characters to spaces - for (int i = 0; i < (len >= 0 ? len : WORKING_BUFFER_SIZE); i++) { - final int ch = working[i]; - if (ch >= 1 && ch <= 32 && ch != 10 && ch != 13) { - working[i] = ' '; - } - } - } - } - - /** - * Saves specified character to the temporary buffer. - * - * @param ch - */ - private void save(final char ch) { - if (savedLen >= saved.length) { - final char newSaved[] = new char[saved.length + 512]; - System.arraycopy(saved, 0, newSaved, 0, saved.length); - saved = newSaved; - } - saved[savedLen++] = ch; - } - - /** - * Saves character at current runtime position to the temporary buffer. - */ - private void saveCurrent() { - if (!isAllRead()) { - save(working[pos]); - } - } - - /** - * Saves specified number of characters at current runtime position to the - * temporary buffer. - * - * @throws IOException - */ - private void saveCurrent(int size) throws IOException { - readIfNeeded(size); - int pos = this.pos; - while (!isAllRead() && (size > 0)) { - save(working[pos]); - pos++; - size--; - } - } - - private void saveCurrentSafe() { - save(working[pos]); - } - - /** - * Skips whitespaces at current position and moves foreward until - * non-whitespace character is found or the end of content is reached. - * - * @throws IOException - */ - private void skipWhitespaces() throws IOException { - while (!isAllRead() && isWhitespaceSafe()) { - saveCurrentSafe(); - go(); - } - } - - /** - * Starts parsing HTML. - * - * @throws IOException - */ - public void start() throws IOException { - // initialize runtime values - currentTagToken = null; - tokenList.clear(); - asExpected = true; - isScriptContext = false; - - boolean isLateForDoctype = false; - - this.pos = WORKING_BUFFER_SIZE; - readIfNeeded(0); - - boolean isScriptEmpty = true; - - while (!isAllRead()) { - // resets all the runtime values - savedLen = 0; - currentTagToken = null; - asExpected = true; - - // this is enough for making decision - readIfNeeded(10); - - if (isScriptContext) { - if (startsWith("'))) { - tagEnd(); - } else if (isScriptEmpty && startsWithSimple("