Java tutorial
/* * Copyright (c) 2003- Shinji Kashihara. All rights reserved. * This program are made available under the terms of the Common Public License * v1.0 which accompanies this distribution, and is available at cpl-v10.html. */ package mergedoc.core; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * ????? * <p> * @author Shinji Kashihara */ public class Comment { /** */ private static final Log log = LogFactory.getLog(Comment.class); /** ??? */ private static final int DEFAULT_WIDTH = Integer.MAX_VALUE;; //----------------------------------------- // API ??? //----------------------------------------- /** ?? */ private final Signature sig; /** */ private String docBody; /** deprecated */ private String deprecate; /** see ? */ private List<String> sees; /** since ? */ private List<String> sinces; /** param ? */ private List<String> params; /** return ? */ private List<String> returns; /** throws ? */ private List<String> throwses; //----------------------------------------- // Java ??? //----------------------------------------- /** ? Java ?? */ private String srcBody; /** author ? */ private List<String> srcAuthors; /** version ? */ private List<String> srcVersions; /** serial ? */ private List<String> srcSerials; /** serialField ? */ private List<String> srcSerialFields; /** serialData ? */ private List<String> srcSerialDatas; /** spec ?JSR No.? */ private List<String> srcSpecs; /** * ?? * <p> * @param sig ?? */ public Comment(Signature sig) { this.sig = sig; } /** * ??? * <p> * @param docBody */ public void setDocumentBody(String docBody) { this.docBody = formatHTML(docBody); } /** * ??? HTML ??? * <p> * @param comment * @return HTML ? */ private String formatHTML(String comment) { // HTML ????????? boolean hasHtmlTag = comment.contains("<"); // HTML ?? if (hasHtmlTag) { Pattern pat = PatternCache.getPattern("</?\\w+"); Matcher mat = pat.matcher(comment); StringBuffer sb = new StringBuffer(); while (mat.find()) { String tag = mat.group().toLowerCase(); mat.appendReplacement(sb, tag); } mat.appendTail(sb); comment = sb.toString(); } comment = FastStringUtils.replaceAll(comment, "\\*/", "*/"); comment = FastStringUtils.replaceAll(comment, "\\\\u", "\u"); // comment = FastStringUtils.replaceAll(comment, "(?m)^ ", ""); // HTML ? if (hasHtmlTag) { // </p> comment = FastStringUtils.replace(comment, "</p>", ""); // <blockquote> comment = FastStringUtils.replaceAll(comment, "\\s*(</?blockquote>)\\s*", "\n$1\n"); // <pre> if (comment.contains("<pre>")) { comment = FastStringUtils.replaceAll(comment, "\\s*(</?pre>)\\s*", "\n$1\n"); comment = FastStringUtils.replaceAll(comment, "(<blockquote>)\n(<pre>)", "$1$2"); comment = FastStringUtils.replaceAll(comment, "(</pre>)\n(</blockquote>)", "$1$2"); } // <table> if (comment.contains("<table")) { comment = FastStringUtils.replaceAll(comment, "\\s*(</?table|</?tr>)", "\n$1"); comment = FastStringUtils.replaceAll(comment, "\\s*(<(th|td))", "\n $1"); comment = FastStringUtils.replaceAll(comment, "\\s*(<blockquote>)\n(<table)", "\n\n$1$2"); comment = FastStringUtils.replaceAll(comment, "(</table>)\n(</blockquote>)", "$1$2"); } // <ol> <ul> <li> comment = FastStringUtils.replaceAll(comment, "\\s*(</?(ol|ul|li)>)", "\n$1"); // <p> if (comment.contains("<p>")) { comment = FastStringUtils.replaceAll(comment, "\\s*(<p>)\\s*", "\n\n$1"); comment = FastStringUtils.replaceAll(comment, "(\\s*<p>)+$", ""); } // <br/> if (comment.contains("<br")) { comment = FastStringUtils.replaceAll(comment, "<br\\s*/>", "<br>"); } } // ???? comment = comment.trim(); return comment; } /** * deprecated ??? * <p> * @param comment */ public void addDeprecated(String comment) { comment = formatHTML(comment); deprecate = comment; } /** * see ??? * <p> * @param comment */ public void addSee(String comment) { if (sees == null) { sees = new LinkedList<String>(); } sees.add(comment); } /** * since ??? * <p> * @param comment */ public void addSince(String comment) { if (sinces == null) { sinces = new LinkedList<String>(); } sinces.add(comment); } /** * param ??? * <p> * @param comment */ public void addParam(String name, String desc) { if (params == null) { params = new LinkedList<String>(); } desc = formatHTML(desc); params.add(name + " " + desc); } /** * return ??? * <p> * @param comment */ public void addReturn(String comment) { if (returns == null) { returns = new LinkedList<String>(); } comment = formatHTML(comment); returns.add(comment); } /** * throws ??? * <p> * @param comment */ public void addThrows(String comment) { if (throwses == null) { throwses = new LinkedList<String>(); } comment = formatHTML(comment); throwses.add(comment); } /** * ? Java ??? * ????????? * <p> * @param srcBody ? Java */ public void setSourceBody(String srcBody) { // @exception @throws ?? this.srcBody = FastStringUtils.replaceAll(srcBody, "\\s@exception\\s", " @throws "); // author ????? srcAuthors = createWrapTagList("@author"); // throws ????? // {@inheritDoc} ???????API ? // ???{@inheritDoc} ?????? List<String> srcThrowses = createWrapTagList("@throws"); if (srcThrowses != null && throwses != null) { for (String src : srcThrowses) { Pattern pat = PatternCache.getPattern("(?s)(\\w+)\\s+(.*)"); Matcher mat = pat.matcher(src); if (mat.find() && mat.group(2).contains("@inheritDoc")) { String exceptionClassName = mat.group(1); for (int i = 0; i < throwses.size(); i++) { String doc = throwses.get(i); if (doc.startsWith(exceptionClassName)) { throwses.set(i, exceptionClassName + " {@inheritDoc}"); } } } } } // ?????????? srcVersions = createTagList("@version"); srcSerials = createTagList("@serial"); srcSerialFields = createTagList("@serialField"); srcSerialDatas = createTagList("@serialData"); srcSpecs = createTagList("@spec"); } /** * ? Java ??? Javadoc ?????? * ???????????????? * <p> * @param tagName ?? * @return ? */ private List<String> createWrapTagList(String tagName) { List<String> tagValues = null; if (srcBody.contains(tagName)) { String undeco = FastStringUtils.replaceAll(srcBody, "(?m)^ *\\* *", ""); Pattern pat = PatternCache.getPattern("(?s)" + tagName + " *(.*?)([^\\{]@\\w+|/\\s*$)"); Matcher mat = pat.matcher(undeco); for (int start = 0; mat.find(start); start = mat.end(1)) { if (tagValues == null) { tagValues = new LinkedList<String>(); } tagValues.add(mat.group(1)); } } return tagValues; } /** * ? Java ??? Javadoc ?????? * ????????????? * <p> * @param tagName ?? * @return ? */ private List<String> createTagList(String tagName) { List<String> tagValues = null; if (srcBody.contains(tagName)) { Pattern pat = PatternCache.getPattern(" +" + tagName + " *(.*)\n"); Matcher mat = pat.matcher(srcBody); while (mat.find()) { if (tagValues == null) { tagValues = new LinkedList<String>(); } tagValues.add(mat.group(1)); } } return tagValues; } /** * ?????? * <p> * @return ?? */ public String buildComment() { if (srcBody == null) { throw new IllegalStateException("Source comment is null. require #setSourceComment."); } // Java ? @deprecated ??????? // API ?? @deprecated ???? // ?????????????? if (!srcBody.contains("@deprecated")) { deprecate = null; } // Java ???????? // ?? API ? Java ????? if (FastStringUtils.matches(srcBody, "(?s)\\s*/\\*\\*[\\s\\*]*@.*")) { docBody = null; params = omitTags(params, "@param"); returns = omitTag(returns, "@return"); throwses = omitTags(throwses, "@throws"); sees = omitTags(sees, "@see"); } // Java ???? int decoSize = 2; int originDecoHeight = FastStringUtils.heightOf(srcBody); if (originDecoHeight <= 0) { // throw new IllegalStateException( // "Illegal comment height " + originDecoHeight + "\n" + srcBody); } int originHeight = originDecoHeight; if (originDecoHeight > decoSize) { originHeight = originDecoHeight - decoSize; } String indent = FastStringUtils.replaceFirst(srcBody, "(?s)^( *?)/\\*\\*.*", "$1"); // API ?? pre Java ???? if (docBody != null) { replacePreBody(); } // Java ???? 2 ?? if (originDecoHeight <= 2) { StringBuilder sb = new StringBuilder(); sb.append(indent); sb.append("/** "); if (docBody != null && docBody.length() > 0) { String str = FastStringUtils.replaceAll(docBody, "\n", ""); sb.append(str); } else if (sinces != null && sinces.size() > 0) { sb.append("@since "); String str = sinces.get(0); str = FastStringUtils.replace(str, "\n", ""); sb.append(str); } if (originDecoHeight == 2) { sb.append("\n"); sb.append(indent); } sb.append(" */"); if (srcBody.endsWith("\n")) { sb.append("\n"); } return sb.toString(); } // ?? int width = DEFAULT_WIDTH - indent.length() - OutputComment.LINE_PREFIX.length(); OutputComment o = new OutputComment(originHeight, width); String decoComment = o.toString(); if (decoComment.length() > 0) { if (o.resultHeight() != o.originHeight) { decoComment = resizeComment(o, decoComment); } decoComment = FastStringUtils.replaceAll(decoComment, "(?m)^", indent); decoComment = FastStringUtils.replaceAll(decoComment, "(?m)^ +$", ""); } return decoComment; } /** * Java ??? Javadoc * ?????????????API ?? * ??????????? Java ?? * API ?????????? * API ????? Java ????? * null ??? * <p> * ??????? {@link #omitTags(List, String)} * ??????? * <p> * @param docTagList API ? * @param tagName ?? * @return ? */ private List<String> omitTag(List<String> docTagList, String tagName) { if (docTagList != null && docTagList.size() > 0) { List<String> srcTagList = createWrapTagList(tagName); if (srcTagList == null || srcTagList.size() == 0) { return null; } } return docTagList; } /** * Java ??? Javadoc * ?????????????API ?? * ??????????? Java ?? * API ?????????? * API ?? Java ??? * API ????????Java ??????? * <p> * @param docTagList API ? * @param tagName ?? * @return ? */ private List<String> omitTags(List<String> docTagList, String tagName) { if (docTagList != null && docTagList.size() > 0) { List<String> srcTagList = createWrapTagList(tagName); if (srcTagList == null || srcTagList.size() == 0) { return null; } if (docTagList.size() > srcTagList.size()) { List<String> names = new ArrayList<String>(); for (String src : srcTagList) { String name = FastStringUtils.replaceFirst(src, "(?s)^(\\S+)\\s.*$", "$1"); names.add(name); } List<String> newTagList = new ArrayList<String>(); for (String doc : docTagList) { String name = FastStringUtils.replaceFirst(doc, "(?s)^(\\S+)\\s.*$", "$1"); if (names.contains(name)) { newTagList.add(doc); } } return newTagList; } } return docTagList; } /** * JDK1.5 API ???? pre HTML ?? * ???????????Java ????? * ????pre ????????????? API ? * ??? */ private void replacePreBody() { // pre ?????????? if (!docBody.contains("<pre>")) { return; } // Java ? pre ?? LinkedList<String> pres = null; String commentBody = FastStringUtils.replaceAll(srcBody, "(?m)^\\s*\\*( |)", ""); Pattern pat = PatternCache.getPattern("(?s)(<pre>\n)(.+?)(\n</pre>)"); Matcher mat = pat.matcher(commentBody); while (mat.find()) { if (pres == null) { pres = new LinkedList<String>(); } pres.add(mat.group(2)); } if (pres == null) { return; } // API ? pre ?? Java ??? Matcher descMatcher = pat.matcher(docBody); StringBuffer sb = new StringBuffer(); while (descMatcher.find()) { // pre ?????????? if (pres.size() == 0) { return; } String value = FastStringUtils.quoteReplacement(pres.removeFirst()); descMatcher.appendReplacement(sb, "$1" + value + "$3"); } descMatcher.appendTail(sb); // pre ???????? if (pres.size() == 0) { docBody = sb.toString(); } } /** * ?????? */ private class OutputComment { final static String LINE_PREFIX = " * "; final int originHeight; final int initWidth; int width; String comment; boolean enabledFirstLine; OutputComment(int originHeight, int width) { this.originHeight = originHeight; this.initWidth = width; this.width = width; build(); } void build() { this.comment = formatComment(width, originHeight); } void rebuild() { resetWidth(); build(); } int resultHeight() { if (enabledFirstLine) { return FastStringUtils.heightOf(comment) - 1; } else { return FastStringUtils.heightOf(comment); } } void resetWidth() { width = initWidth; } @Override public String toString() { String str = comment; if (str.length() > 0) { StringBuilder sb = new StringBuilder(); str = FastStringUtils.replaceAll(str, "(?m)^", LINE_PREFIX); if (enabledFirstLine) { sb.append("/**"); str = FastStringUtils.replaceFirst(str, "^ \\*", ""); } else { sb.append("/**\n"); } sb.append(str); sb.append(" */\n"); str = sb.toString(); } return str; } } /** * ??? * * @param o ? * @return ???? */ private String resizeComment(OutputComment o, String decoComment) { // ???????????? if (o.resultHeight() > o.originHeight) { shrinkComment(o); // ????????? // docBody ? <pre> ??????? if (docBody != null && o.resultHeight() > o.originHeight && docBody.contains("\n")) { StringBuilder sb = new StringBuilder(); boolean inPreTag = false; for (int i = 0; i < docBody.length(); i++) { char c = docBody.charAt(i); sb.append(c); String buf = sb.toString(); if (buf.endsWith("<pre>")) { inPreTag = true; sb.insert(sb.length() - 5, '\n'); } else if (buf.endsWith("</pre>")) { inPreTag = false; sb.append('\n'); } if (c == '\n' && !inPreTag) { sb.deleteCharAt(sb.length() - 1); } } docBody = sb.toString(); o.rebuild(); if (o.resultHeight() > o.originHeight) { shrinkComment(o); } } // ???????????? 1 ?????? // ???? JDK5.0 API ? shrinkComment ? // ????????? // BasicEditorPaneUI#installUI(JComponent) ?????? if (docBody != null && o.resultHeight() > o.originHeight) { int pos = docBody.indexOf(''); if (pos != -1) { docBody = docBody.substring(0, pos + 1); o.rebuild(); if (o.resultHeight() > o.originHeight) { shrinkComment(o); } } } // ??????????... if (o.resultHeight() > o.originHeight) { // JDK5.0 API ??????? log.warn(sig + " ???????????\n" + "-------------------------------------------------\n" + ":\n" + srcBody + "\n:\n" + o.toString() + "-------------------------------------------------\n"); return srcBody; } decoComment = o.toString(); } // ????????????? if (o.resultHeight() < o.originHeight) { expandComment(o); decoComment = o.toString(); } // ???????? if (o.resultHeight() < o.originHeight) { int sub = o.originHeight - o.resultHeight(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < sub; i++) { sb.append("\n"); } decoComment = sb + decoComment; } return decoComment; } /** * ??????? * <p> * @param o */ private void shrinkComment(OutputComment o) { // ? Java ???? String emptyDescRegex = "(?s)\\s*/\\*\\*\\s*\\*\\s*@.*"; if (FastStringUtils.matches(srcBody, emptyDescRegex)) { docBody = null; o.build(); if (o.resultHeight() <= o.originHeight) { return; } } // ? Java ???? // -> buildComment ????????????????? boolean b1 = shrinkTagList("@see", sees); boolean b2 = shrinkTagList("@throws", throwses); boolean b3 = shrinkTagList("@param", params); boolean b4 = shrinkTagList("@return", returns); if (b1 || b2 || b3 || b4) { o.build(); if (o.resultHeight() <= o.originHeight) { return; } } // ? int height = o.resultHeight(); // ? <p> ???? Pattern pTagPat = PatternCache.getPattern("<p>\n\n(<p>)"); if (o.comment.contains("<p>\n\n<p>")) { StringBuffer sb = new StringBuffer(); Matcher pTagMat = pTagPat.matcher(o.comment); while (height > o.originHeight && pTagMat.find()) { pTagMat.appendReplacement(sb, "$1"); height -= 2; } pTagMat.appendTail(sb); o.comment = sb.toString(); if (height <= o.originHeight) { return; } } // <th?<td?</tr ??? Pattern tdTagPat = PatternCache.getPattern("\\s+(<(t[hd]|/tr))"); if (o.comment.contains("<table")) { StringBuffer sb = new StringBuffer(); Matcher tdTagMat = tdTagPat.matcher(o.comment); while (height > o.originHeight && tdTagMat.find()) { tdTagMat.appendReplacement(sb, "$1"); height--; } tdTagMat.appendTail(sb); o.comment = sb.toString(); if (height <= o.originHeight) { return; } } // <li?</ul?</ol ??? Pattern liTagPat = PatternCache.getPattern("\\s+(<(li|/[uo]l))"); if (o.comment.contains("<li")) { StringBuffer sb = new StringBuffer(); Matcher liTagMat = liTagPat.matcher(o.comment); while (height > o.originHeight && liTagMat.find()) { liTagMat.appendReplacement(sb, "$1"); height--; } liTagMat.appendTail(sb); o.comment = sb.toString(); if (height <= o.originHeight) { return; } } // Pattern emptyLinePat = PatternCache.getPattern("(?m)^\\s*?\n"); Matcher emptyLineMat = emptyLinePat.matcher(o.comment); StringBuffer sb = new StringBuffer(); while (height > o.originHeight && emptyLineMat.find()) { emptyLineMat.appendReplacement(sb, ""); height--; } emptyLineMat.appendTail(sb); o.comment = sb.toString(); if (height <= o.originHeight) { return; } // Java ? 1 ???? String firstLineRegex = "(?s)\\s*/\\*\\*\\s*\n.*"; if (!FastStringUtils.matches(srcBody, firstLineRegex)) { o.enabledFirstLine = true; if (o.resultHeight() <= o.originHeight) { return; } } // ??? final int maxWidth = 160; while (o.resultHeight() > o.originHeight && o.width < maxWidth) { o.build(); if (o.resultHeight() <= o.originHeight) { return; } if (o.comment.contains("<")) { o.comment = pTagPat.matcher(o.comment).replaceAll("$1"); if (o.resultHeight() <= o.originHeight) { return; } o.comment = tdTagPat.matcher(o.comment).replaceAll("$1"); if (o.resultHeight() <= o.originHeight) { return; } o.comment = liTagPat.matcher(o.comment).replaceAll("$1"); if (o.resultHeight() <= o.originHeight) { return; } } o.comment = emptyLinePat.matcher(o.comment).replaceAll(""); if (o.resultHeight() <= o.originHeight) { return; } if (o.width < 100) { o.width += 4; } else { o.width += 8; } } } /** * ??? Java ??????? * <p> * @param tagRegex ?? * @param tagList * @return ???? true */ private boolean shrinkTagList(String tagRegex, List<String> tagList) { if (tagList != null) { int tagCount = FastStringUtils.split(srcBody, "\\s" + tagRegex + "\\s", -1).length - 1; if (tagCount == 0) { tagList.clear(); return true; } } return false; } /** * ??????? * <p> * @param o */ private void expandComment(OutputComment o) { // HTML ?????????? if (!o.comment.contains("<")) { return; } // ? int height = o.resultHeight(); // <pre>?<blockquote>?<ol>?<ul> ?? StringBuffer sb = new StringBuffer(); Pattern pat = PatternCache.getPattern("([^\n])(\n(<blockquote>)?<pre>|\n<(blockquote|ol|ul)>)"); Matcher mat = pat.matcher(o.comment); while (height < o.originHeight && mat.find()) { mat.appendReplacement(sb, "$1\n$2"); height++; } mat.appendTail(sb); o.comment = sb.toString(); if (height == o.originHeight) { return; } // </pre>?</blockquote>?</ol>?</ul> ?? sb = new StringBuffer(); pat = PatternCache.getPattern("(</pre>(</blockquote>)?\n|</(blockquote|ol|ul)>\n)([^\n])"); mat = pat.matcher(o.comment); while (height < o.originHeight && mat.find()) { mat.appendReplacement(sb, "$1\n$4"); height++; } mat.appendTail(sb); o.comment = sb.toString(); if (height == o.originHeight) { return; } } /** * ??????? * <p> * @param width * @param originHeight * @return ??? */ private String formatComment(int width, int originHeight) { StringBuilder sb = new StringBuilder(); // ??? if (docBody != null && docBody.length() > 0) { if (originHeight == 1) { sb.append(FastStringUtils.replaceAll(docBody, "\n", "")); sb.append("\n"); return sb.toString(); } sb.append(adjustWidth(docBody, width)); sb.append("\n"); } // deprecated ??? if (deprecate != null && deprecate.length() > 0) { String depre = "@deprecated " + deprecate; sb.append(adjustWidth(depre, width)); sb.append("\n"); } appendTo("@author ", srcAuthors, sb, width); appendTo("@version ", srcVersions, sb, width); // param ??? if (params != null && params.size() > 0) { // name ? int paramsSize = params.size(); int[] nameLens = new int[paramsSize]; int nameLenMax = 3; final int nameLenLimit = 12; for (int i = 0; i < paramsSize; i++) { String comment = params.get(i); String name = FastStringUtils.replaceFirst(comment, "(?s)(\\w+)\\s.*", "$1"); nameLens[i] = name.length(); if (nameLens[i] > nameLenMax && nameLens[i] < nameLenLimit) { nameLenMax = nameLens[i]; } } // 2 ??? String tag = "@param "; int indentCnt = tag.length() + nameLenMax + 1; StringBuilder indent = new StringBuilder(indentCnt); for (; indentCnt > 0; indentCnt--) { indent.append(' '); } // ?? for (int i = 0; i < paramsSize; i++) { int spaceCnt = nameLenMax - nameLens[i]; StringBuilder space = new StringBuilder(); for (; spaceCnt > 0; spaceCnt--) { space.append(' '); } String comment = params.get(i); comment = FastStringUtils.replaceFirst(comment, "(?s)(\\w+)\\s+(.*)", "$1" + space + " $2"); comment = adjustWidth(comment, width - tag.length()); StringTokenizer st = new StringTokenizer(comment, "\n"); // 1 sb.append(tag); if (st.hasMoreTokens()) { sb.append(st.nextToken()); } sb.append("\n"); // 2 ? while (st.hasMoreTokens()) { sb.append(indent); sb.append(st.nextToken()); sb.append("\n"); } } } appendTo("@return ", returns, sb, width); appendTo("@throws ", throwses, sb, width); appendTo("@serialField", srcSerialFields, sb, width); appendTo("@serialData", srcSerialDatas, sb, width); appendTo("@see ", sees, sb, width); appendTo("@since ", sinces, sb, width); appendTo("@serial ", srcSerials, sb, width); appendTo("@spec ", srcSpecs, sb, width); String str = sb.toString(); str = FastStringUtils.replaceFirst(str, "\n\n$", "\n"); return str; } /** * ?????? * ??????? * <p> * @param tag * @param tagList ? * @param sb ? * @param width */ private void appendTo(String tag, List<String> tagList, StringBuilder sb, int width) { if (tagList == null) { return; } for (String comment : tagList) { comment = adjustWidth(comment, width - tag.length()); StringTokenizer st = new StringTokenizer(comment, "\n"); sb.append(tag); if (st.hasMoreTokens()) { sb.append(st.nextToken()); } sb.append("\n"); while (st.hasMoreTokens()) { sb.append(FastStringUtils.replaceAll(tag, ".", " ")); sb.append(st.nextToken()); sb.append("\n"); } } } /** * ????? * <p> * @param value ? * @param width ?? * @return ?? */ private String adjustWidth(String value, int width) { // 1 ???? if (value.getBytes().length < width) { return value + "\n"; } List<String> lineValues = FastStringUtils.splitLine(value); StringBuilder resultBuf = new StringBuilder(); boolean preTagArea = false; final int longWordWidth = width - 20; // ??????? pre ? // ?????? pre ?? for (String lineValue : lineValues) { if (lineValue.equals("")) { resultBuf.append("\n"); continue; } // pre if (lineValue.startsWith("</pre>")) { preTagArea = false; } else if (lineValue.startsWith("<pre>")) { preTagArea = true; } if (preTagArea) { resultBuf.append(lineValue); resultBuf.append("\n"); if (lineValue.endsWith("</pre>")) { preTagArea = false; } continue; } if (lineValue.endsWith("<pre>")) { preTagArea = true; } // ???? if (lineValue.getBytes().length < width) { resultBuf.append(lineValue); resultBuf.append("\n"); continue; } // <table ???? summary ??? if (lineValue.contains("<table")) { String s = FastStringUtils.replaceFirst(lineValue, "(?i)\\s(summary=)", "\n$1"); resultBuf.append(s); resultBuf.append("\n"); continue; } // ??? String multiLineValue = FastStringUtils.replaceAll(lineValue, "\\s?(\\p{Graph}{" + longWordWidth + ",})", "\n$1"); StringTokenizer st = new StringTokenizer(multiLineValue, "\n"); while (st.hasMoreTokens()) { lineValue = st.nextToken(); wrap(lineValue, resultBuf, width); } } return resultBuf.toString(); } /** * ???????????? * ????????? * <p> * @param lineValue * @param resultBuf ????? * @param width ?? */ private void wrap(String lineValue, StringBuilder resultBuf, int width) { final int minWidth = width - 10; final int maxWidth = width + 10; final int ADJUST_SKIP_WIDTH = width + 4; final int lastPos = lineValue.length() - 1; final String PUNCTS = "??)}"; final String PARTICLES = "???????"; StringBuilder buf = new StringBuilder(); int bufLen = 0; for (int pos = 0; pos < lastPos; pos++) { if (bufLen == 0) { String after = lineValue.substring(pos, lastPos); int afterLen = after.getBytes().length; if (afterLen <= ADJUST_SKIP_WIDTH) { buf.append(after); break; } } char c = lineValue.charAt(pos); int cLen = String.valueOf(c).getBytes().length; bufLen += cLen; boolean isChangeLine = false; if (bufLen > minWidth) { // ??????????? if (c == ' ') { isChangeLine = true; buf.append('\n'); } else if (PUNCTS.indexOf(c) != -1 || PARTICLES.indexOf(c) != -1) { char next = lineValue.charAt(pos + 1); if (PUNCTS.indexOf(next) == -1 && next != ' ' && next != '.') { isChangeLine = true; buf.append(c); buf.append('\n'); } } else if (bufLen > width) { // ?????? // ??????????? if (c == '<' || cLen > 1) { isChangeLine = true; buf.append('\n'); buf.append(c); } else if (bufLen > maxWidth) { // ?????? // ???? for (int bPos = buf.length() - 1; bPos > 0; bPos--) { char bc = buf.charAt(bPos); if (bc == ' ') { buf.replace(bPos, bPos + 1, "\n"); bufLen = buf.substring(bPos + 1).getBytes().length; break; } else { int bcLen = String.valueOf(bc).getBytes().length; if (bcLen > 1) { buf.insert(bPos + 1, '\n'); bufLen = buf.substring(bPos + 2).getBytes().length; break; } } } } } } if (isChangeLine) { resultBuf.append(buf); buf = new StringBuilder(); bufLen = 0; } else { buf.append(c); } } buf.append(lineValue.charAt(lastPos)); resultBuf.append(buf); resultBuf.append('\n'); } }