⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sourceformatter.java

📁 HTML解析器是一个Java库
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
	 * @param indentAllElements  specifies whether all elements are to be indented.
	 * @return this <code>SourceFormatter</code> instance, allowing multiple property setting methods to be chained in a single statement. 
	 * @see #getIndentAllElements()
	 */
	public SourceFormatter setIndentAllElements(final boolean indentAllElements) {
		this.indentAllElements=indentAllElements;
		return this;
	}

	/**
	 * Indicates whether all elements are to be indented, including {@linkplain HTMLElements#getInlineLevelElementNames() inline-level elements} and those with preformatted contents.
	 * <p>
	 * See the {@link #setIndentAllElements(boolean)} method for a full description of this property.
	 * 
	 * @return <code>true</code> if all elements are to be indented, otherwise <code>false</code>.
	 */
	public boolean getIndentAllElements() {
		return indentAllElements;
	}
	
	/**
	 * Sets the string to be used to represent a <a target="_blank" href="http://en.wikipedia.org/wiki/Newline">newline</a> in the output.
	 * <p>
	 * The default is to use the same new line string as is used in the source document, which is determined via the {@link Source#getNewLine()} method.
	 * If the source document does not contain any new lines, a "best guess" is made by either taking the new line string of a previously parsed document,
	 * or using the value from the static {@link Config#NewLine} property.
	 * <p>
	 * Specifying a <code>null</code> argument resets the property to its default value, which is to use the same new line string as is used in the source document.
	 * 
	 * @param newLine  the string to be used to represent a <a target="_blank" href="http://en.wikipedia.org/wiki/Newline">newline</a> in the output, may be <code>null</code>.
	 * @return this <code>SourceFormatter</code> instance, allowing multiple property setting methods to be chained in a single statement. 
	 * @see #getNewLine()
	 */
	public SourceFormatter setNewLine(final String newLine) {
		this.newLine=newLine;
		return this;
	}

	/**
	 * Returns the string to be used to represent a <a target="_blank" href="http://en.wikipedia.org/wiki/Newline">newline</a> in the output.
	 * <p>
	 * See the {@link #setNewLine(String)} method for a full description of this property.
	 *
	 * @return the string to be used to represent a <a target="_blank" href="http://en.wikipedia.org/wiki/Newline">newline</a> in the output.
	 */
	public String getNewLine() {
		if (newLine==null) newLine=segment.source.getBestGuessNewLine();
		return newLine;
	}

	/** This class does the actual work, but is first passed final copies of all the parameters for efficiency. */
	private static final class Processor {
		private final Segment segment;
		private final CharSequence sourceText;
		private final String indentString;
		private final boolean tidyTags;
		private final boolean collapseWhiteSpace;
		private final boolean removeLineBreaks; // Indicates whether all non-essential line breaks are removed. Must be used with collapseWhiteSpace=true.
		private final boolean indentAllElements;
		private final boolean indentScriptElements; // at present this parameter is tied to indentAllElements.  SCRIPT elements need to be inline to keep functional equivalency of output
		private final String newLine;
	
		private Appendable appendable;
		private Tag nextTag;
		private int index;
	
		public Processor(final Segment segment, final String indentString, final boolean tidyTags, final boolean collapseWhiteSpace, final boolean removeLineBreaks, final boolean indentAllElements, final boolean indentScriptElements, final String newLine) {
			this.segment=segment;
			sourceText=segment.source.toString();
			this.indentString=indentString;
			this.tidyTags=tidyTags;
			this.collapseWhiteSpace=collapseWhiteSpace || removeLineBreaks;
			this.removeLineBreaks=removeLineBreaks;
			this.indentAllElements=indentAllElements;
			this.indentScriptElements=indentScriptElements;
			this.newLine=newLine;
		}
	
		public void appendTo(final Appendable appendable) throws IOException {
			this.appendable=appendable;
			if (segment instanceof Source) ((Source)segment).fullSequentialParse();
			nextTag=segment.source.getNextTag(segment.begin);
			index=segment.begin;
			appendContent(segment.end,segment.getChildElements(),0);
		}
	
		private void appendContent(final int end, final List<Element> childElements, final int depth) throws IOException {
			assert index<=end;
			for (Element element : childElements) {
				final int elementBegin=element.begin;
				if (elementBegin>=end) break;
				if (indentAllElements) {
					appendText(elementBegin,depth);
					appendElement(element,depth,end,false,false);
				} else {
					if (inlinable(element)) continue; // skip over elements that can be inlined.
					appendText(elementBegin,depth);
					final String elementName=element.getName();
					if (elementName==HTMLElementName.PRE || elementName==HTMLElementName.TEXTAREA) {
						appendElement(element,depth,end,true,true);
					} else if (elementName==HTMLElementName.SCRIPT) {
						appendElement(element,depth,end,true,false);
					} else {
						appendElement(element,depth,end,false,!removeLineBreaks && containsOnlyInlineLevelChildElements(element));
					}
				}
			}
			appendText(end,depth);
			assert index==end;
		}
	
		private boolean inlinable(final Element element) {
			// returns true if the specified element should be inlined
			final StartTagType startTagType=element.getStartTag().getStartTagType();
			if (startTagType==StartTagType.DOCTYPE_DECLARATION) return false;
			if (startTagType!=StartTagType.NORMAL) return true;
			// element is a normal type
			final String elementName=element.getName();
			if (elementName==HTMLElementName.SCRIPT) return !indentScriptElements;
			if (removeLineBreaks && !HTMLElements.getElementNames().contains(elementName)) return true; // inline non-HTML elements if removing line breaks
			if (!HTMLElements.getInlineLevelElementNames().contains(elementName)) return false;
			// element is inline type
			if (removeLineBreaks) return true;
			return containsOnlyInlineLevelChildElements(element); // only inline if it doesn't illegally contain non-inline elements
		}
	
		private void appendText(final int end, int depth) throws IOException {
			assert index<=end;
			if (index==end) return;
			while (Segment.isWhiteSpace(sourceText.charAt(index))) if (++index==end) return; // trim whitespace.
			appendIndent(depth);
			if (collapseWhiteSpace) {
				appendTextCollapseWhiteSpace(end,depth);
			} else {
				appendTextInline(end,depth,false);
			}
			appendFormattingNewLine();
			assert index==end;
		}
	
		private void appendElement(final Element element, final int depth, final int end, final boolean preformatted, boolean renderContentInline) throws IOException {
			assert index==element.begin;
			assert index<end;
			final StartTag startTag=element.getStartTag();
			final EndTag endTag=element.getEndTag();
			appendIndent(depth);
			appendTag(startTag,depth,end);
			if (index==end) {
				appendFormattingNewLine();
				assert index==Math.min(element.end,end) : index;
				return;
			}
			if (!renderContentInline) appendFormattingNewLine();
			int contentEnd=element.getContentEnd();
			if (end<contentEnd) contentEnd=end;
			if (index<contentEnd) {
				if (preformatted) {
					if (renderContentInline) {
						// Preformatted element such as PRE, TEXTAREA
						appendContentPreformatted(contentEnd,depth);
					} else {
						// SCRIPT element
						appendIndentedScriptContent(contentEnd,depth+1);
					}
				} else {
					if (renderContentInline) {
						// Element contains only inline-level elements, so don't bother putting start and end tags on separate lines
						if (collapseWhiteSpace) {
							appendTextCollapseWhiteSpace(contentEnd,depth);
						} else {
							if (!appendTextInline(contentEnd,depth,true)) {
								appendFormattingNewLine();
								renderContentInline=false;
							}
						}
					} else {
						appendContent(contentEnd,element.getChildElements(),depth+1);
					}
				}
			}
			if (endTag!=null && end>endTag.begin) {
				if (!renderContentInline) appendIndent(depth);
				assert index==endTag.begin;
				appendTag(endTag,depth,end);
				appendFormattingNewLine();
			} else if (renderContentInline) {
				appendFormattingNewLine();
			}
			assert index==Math.min(element.end,end) : index;
		}
	
		private void updateNextTag() {
			// ensures that nextTag is up to date
			while (nextTag!=null) {
				if (nextTag.begin>=index) return;
				nextTag=nextTag.getNextTag();
			}
		}
	
		private void appendIndentedScriptContent(final int end, final int depth) throws IOException {
			assert index<end;
			if (removeLineBreaks) {
				appendTextRemoveIndentation(end);
				assert index==end;
				return;
			}
			int startOfLinePos=getStartOfLinePos(end,false);
			if (index==end) return;
			if (startOfLinePos==-1) {
				// Script started on same line as start tag.  Use the start of the next line to determine the original indent.
				appendIndent(depth);
				appendLineKeepWhiteSpace(end,depth);
				appendEssentialNewLine();
				if (index==end) return;
				startOfLinePos=getStartOfLinePos(end,true);
				if (index==end) return;
			}
			appendTextPreserveIndentation(end,depth,index-startOfLinePos);
			appendEssentialNewLine();
			assert index==end;
		}
	
		private boolean appendTextPreserveIndentation(final int end, final int depth) throws IOException {
			// returns true if all text was on one line, otherwise false
			assert index<end;
			if (removeLineBreaks) return appendTextRemoveIndentation(end);
			// Use the start of the next line to determine the original indent.
			appendLineKeepWhiteSpace(end,depth);
			if (index==end) return true;
			int startOfLinePos=getStartOfLinePos(end,true);
			if (index==end) return true;
			appendEssentialNewLine();
			appendTextPreserveIndentation(end,depth+1,index-startOfLinePos);
			assert index==end;
			return false;
		}
	
		private void appendTextPreserveIndentation(final int end, final int depth, final int originalIndentLength) throws IOException {
			assert index<end;
			appendIndent(depth);
			appendLineKeepWhiteSpace(end,depth);
			while (index!=end) {
				// Skip over the original indent:
				for (int x=0; x<originalIndentLength; x++) {
					final char ch=sourceText.charAt(index);
					if (!(ch==' ' || ch=='\t')) break;
					if (++index==end) return;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -