001: /*
002: * Copyright (c) 2001 - 2005 ivata limited.
003: * All rights reserved.
004: * -----------------------------------------------------------------------------
005: * ivata masks may be redistributed under the GNU General Public
006: * License as published by the Free Software Foundation;
007: * version 2 of the License.
008: *
009: * These programs are free software; you can redistribute them and/or
010: * modify them under the terms of the GNU General Public License
011: * as published by the Free Software Foundation; version 2 of the License.
012: *
013: * These programs are distributed in the hope that they will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: *
017: * See the GNU General Public License in the file LICENSE.txt for more
018: * details.
019: *
020: * If you would like a copy of the GNU General Public License write to
021: *
022: * Free Software Foundation, Inc.
023: * 59 Temple Place - Suite 330
024: * Boston, MA 02111-1307, USA.
025: *
026: *
027: * To arrange commercial support and licensing, contact ivata at
028: * http://www.ivata.com/contact.jsp
029: * -----------------------------------------------------------------------------
030: * $Log: LinkFormat.java,v $
031: * Revision 1.5 2005/10/11 18:54:06 colinmacleod
032: * Fixed some checkstyle and javadoc issues.
033: *
034: * Revision 1.4 2005/10/02 14:06:33 colinmacleod
035: * Added/improved log4j logging.
036: *
037: * Revision 1.3 2005/04/11 14:45:39 colinmacleod
038: * Changed HTMLFormat from an abstract class
039: * into an interface.
040: *
041: * Revision 1.2 2005/04/09 18:04:18 colinmacleod
042: * Changed copyright text to GPL v2 explicitly.
043: *
044: * Revision 1.1 2005/01/06 22:41:01 colinmacleod
045: * Moved up a version number.
046: * Changed copyright notices to 2005.
047: * Updated the documentation:
048: * - started working on multiproject:site docu.
049: * - changed the logo.
050: * Added checkstyle and fixed LOADS of style issues.
051: * Added separate thirdparty subproject.
052: * Added struts (in web), util and webgui (in webtheme) from ivata op.
053: *
054: * Revision 1.3 2004/03/21 21:16:37 colinmacleod
055: * Shortened name to ivata op.
056: *
057: * Revision 1.2 2004/02/01 22:07:32 colinmacleod
058: * Added full names to author tags
059: *
060: * Revision 1.1.1.1 2004/01/27 20:59:48 colinmacleod
061: * Moved ivata op to SourceForge.
062: *
063: * Revision 1.2 2003/10/15 14:13:39 colin
064: * Fixes for XDoclet.
065: *
066: * Revision 1.1 2003/02/24 19:33:33 colin
067: * Moved to new subproject.
068: *
069: * Revision 1.3 2003/02/04 17:43:46 colin
070: * copyright notice
071: *
072: * Revision 1.2 2002/06/21 14:50:09 colin
073: * fixed header documentation
074: * -----------------------------------------------------------------------------
075: */
076: package com.ivata.mask.web.format;
077:
078: import org.apache.log4j.Logger;
079:
080: /**
081: * <p>
082: * This class converts URLs in the text into HTML anchor links.
083: * </p>
084: *
085: * @since ivata masks 0.4 (2002-06-19)
086: * @author Colin MacLeod
087: * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
088: * @version $Revision: 1.5 $
089: */
090: public class LinkFormat implements HTMLFormat {
091: /**
092: * Logger for this class.
093: */
094: private static final Logger logger = Logger
095: .getLogger(LinkFormat.class);
096:
097: /**
098: * <p>
099: * Set by <code>HTMLFormatter</code> if HTML entities are converted (by
100: * <code>HTMLEntityFormat</code>) after this format is applied. This
101: * ensures that our tags are marked to keep, so they do not get converted.
102: * </p>
103: */
104: private boolean convertHTMLEntities = false;
105:
106: /**
107: * <p>
108: * Helper method. The string <code>hTMLText</code> is searched for URLs
109: * which begin with the protocol text provided, and all are converted to
110: * anchor links.
111: * </p>
112: *
113: * @param hTMLText
114: * the text to convert.
115: * @param protocol
116: * the protocol to look for.This can be "http://", for example.
117: * @return a string with all of the URLs of the given protocol converted to
118: * HTML anchor links.
119: */
120: private String convertOneURLType(final String hTMLText,
121: final String protocol) {
122: if (logger.isDebugEnabled()) {
123: logger.debug("convertOneURLType(String hTMLText = "
124: + hTMLText + ", String protocol = " + protocol
125: + ") - start");
126: }
127:
128: int index = 0;
129: int indexLast = 0;
130: StringBuffer returnBuffer = new StringBuffer();
131: String sURL;
132: // find all occurrences of the protocol
133: while ((index = hTMLText.indexOf(protocol, indexLast)) != -1) {
134: // append the string between this and the last protocol
135: if (index != 0) {
136: returnBuffer.append(hTMLText
137: .substring(indexLast, index));
138: }
139: // store the start: we will use it to substring the URL out
140: int indexStart = index;
141: index += protocol.length();
142: // find the first space after the end of the URL
143: int length = hTMLText.length();
144: while ((index < length)
145: && isURLCharacter(hTMLText.charAt(index))) {
146: ++index;
147: }
148: sURL = hTMLText.substring(indexStart, index);
149: indexLast = index;
150: int newIndex;
151: // see if the string is Outlook format, with the URL in <>
152: // afterwards,
153: if ((newIndex = hTMLText.indexOf("<" + sURL + ">",
154: indexLast)) != -1) {
155: int newIndexLast = newIndex + sURL.length() + 2;
156: // check there is only whitespace between the two links
157: while (--newIndex >= indexLast) {
158: if (!Character.isWhitespace(hTMLText
159: .charAt(newIndex))) {
160: break;
161: }
162: }
163: // if we checked all the chars and they were all whitespace,
164: // ignore this outlook link
165: if (newIndex < indexLast) {
166: indexLast = newIndexLast;
167: }
168: }
169: // surround with the IIKEEP: tag so that it does not get converted
170: // later
171: if (convertHTMLEntities) {
172: returnBuffer.append("<IIKEEP:>");
173: }
174: returnBuffer.append("<a href='");
175: returnBuffer.append(sURL);
176: returnBuffer.append("' target='_blank'>");
177: returnBuffer.append(sURL);
178: returnBuffer.append("</a>");
179: if (convertHTMLEntities) {
180: returnBuffer.append("</IIKEEP:>");
181: }
182: }
183: returnBuffer.append(hTMLText.substring(indexLast));
184: String returnString = returnBuffer.toString();
185: if (logger.isDebugEnabled()) {
186: logger.debug("convertOneURLType - end - return value = "
187: + returnString);
188: }
189: return returnString;
190: }
191:
192: /**
193: * <p>
194: * Convert all URLs in the text provided into HTML 'anchor' links.
195: * Currently, this method knows and converts URLs beginning with: <br/>
196: * <ul>
197: * <li>http://</li>
198: * <li>https://</li>
199: * <li>ftp://</li>
200: * </ul>
201: * </p>
202: *
203: * @param hTMLTextParam
204: * HTML text to convert URLs in.
205: * @return formatted text, with all of the URLs converted to HTML anchor
206: * tags.
207: */
208: public final String format(final String hTMLTextParam) {
209: if (logger.isDebugEnabled()) {
210: logger.debug("format(String hTMLTextParam = "
211: + hTMLTextParam + ") - start");
212: }
213:
214: String hTMLText = hTMLTextParam;
215: hTMLText = convertOneURLType(hTMLText, "http://");
216: hTMLText = convertOneURLType(hTMLText, "https://");
217: hTMLText = convertOneURLType(hTMLText, "ftp://");
218:
219: if (logger.isDebugEnabled()) {
220: logger.debug("format(String) - end - return value = "
221: + hTMLText);
222: }
223: return hTMLText;
224: }
225:
226: /**
227: * <p>
228: * Check to see if a character us allowed as part of a URL string.
229: * </p>
230: *
231: * @param checkChar
232: * character to check
233: * @return <code>true</code> if the character may be in a URL, otherwise
234: * <code>false</code>.
235: */
236: private boolean isURLCharacter(final char checkChar) {
237: if (logger.isDebugEnabled()) {
238: logger.debug("isURLCharacter(char checkChar = " + checkChar
239: + ") - start");
240: }
241:
242: boolean returnboolean = (((checkChar >= 'a') && (checkChar <= 'z'))
243: || ((checkChar >= 'A') && (checkChar <= 'Z'))
244: || ((checkChar >= '0') && (checkChar <= '9'))
245: || (checkChar == '%')
246: || (checkChar == '/')
247: || (checkChar == '-')
248: || (checkChar == '_')
249: || (checkChar == ':')
250: || (checkChar == '?')
251: || (checkChar == '.')
252: || (checkChar == ',')
253: || (checkChar == '&')
254: || (checkChar == '#')
255: || (checkChar == '@') || (checkChar == '='));
256: if (logger.isDebugEnabled()) {
257: logger.debug("isURLCharacter(char) - end - return value = "
258: + returnboolean);
259: }
260: return returnboolean;
261: }
262:
263: /**
264: * Allows <code>HTMLFormat</code> to tell this format it should convert
265: * HTML character entities in the display part of the link.
266: *
267: * @param convertHTMLEntitiesParam
268: * if <code>true</code> then character entities will be
269: * converted.
270: */
271: final void setConvertHTMLEntities(
272: final boolean convertHTMLEntitiesParam) {
273: if (logger.isDebugEnabled()) {
274: logger
275: .debug("setConvertHTMLEntities(boolean convertHTMLEntitiesParam = "
276: + convertHTMLEntitiesParam + ") - start");
277: }
278:
279: convertHTMLEntities = convertHTMLEntitiesParam;
280:
281: if (logger.isDebugEnabled()) {
282: logger.debug("setConvertHTMLEntities(boolean) - end");
283: }
284: }
285: }
|