001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH & Co. KG, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.report; 029 030import org.opencms.i18n.CmsEncoder; 031import org.opencms.main.CmsException; 032import org.opencms.util.CmsStringUtil; 033 034import java.util.ArrayList; 035import java.util.List; 036import java.util.Locale; 037import java.util.StringTokenizer; 038 039/** 040 * HTML report output to be used for import / export / publish operations 041 * in the entire OpenCms system.<p> 042 * 043 * @since 6.0.0 044 */ 045public class CmsHtmlReport extends A_CmsReport { 046 047 /** Constant for a HTML linebreak with added "real" line break. */ 048 public static final String LINEBREAK = "<br>"; 049 050 /** 051 * Constant for a HTML linebreak with added "real" line break- 052 * traditional style for report threads that still use XML templates for their output. 053 */ 054 public static final String LINEBREAK_TRADITIONAL = "<br>\\n"; 055 056 /** The list of report objects e.g. String, CmsPageLink, Exception ... */ 057 private List<Object> m_content; 058 059 /** 060 * Counter to remember what is already shown, 061 * indicates the next index of the m_content list that has to be reported. 062 */ 063 private int m_indexNext; 064 065 /** Flag to indicate if an exception should be displayed long or short. */ 066 private boolean m_showExceptionStackTrace; 067 068 /** If set to <code>true</code> nothing is kept in memory. */ 069 private boolean m_transient; 070 071 /** Boolean flag indicating whether this report should generate HTML or JavaScript output. */ 072 private boolean m_writeHtml; 073 074 /** 075 * Constructs a new report using the provided locale for the output language.<p> 076 * 077 * @param locale the locale to use for the output language 078 * @param siteRoot the site root of the user who started this report (may be <code>null</code>) 079 */ 080 public CmsHtmlReport(Locale locale, String siteRoot) { 081 082 this(locale, siteRoot, false, false); 083 } 084 085 /** 086 * Constructs a new report using the provided locale for the output language.<p> 087 * 088 * @param locale the locale to use for the output language 089 * @param siteRoot the site root of the user who started this report (may be <code>null</code>) 090 * @param writeHtml if <code>true</code>, this report should generate HTML instead of JavaScript output 091 * @param isTransient If set to <code>true</code> nothing is kept in memory 092 */ 093 public CmsHtmlReport(Locale locale, String siteRoot, boolean writeHtml, boolean isTransient) { 094 095 init(locale, siteRoot); 096 m_content = new ArrayList<Object>(256); 097 m_showExceptionStackTrace = true; 098 m_writeHtml = writeHtml; 099 m_transient = isTransient; 100 } 101 102 /** 103 * @see org.opencms.report.I_CmsReport#getReportUpdate() 104 */ 105 public synchronized String getReportUpdate() { 106 107 StringBuffer result = new StringBuffer(); 108 int indexEnd = m_content.size(); 109 for (int i = m_indexNext; i < indexEnd; i++) { 110 int pos = m_transient ? 0 : i; 111 Object obj = m_content.get(pos); 112 if ((obj instanceof String) || (obj instanceof StringBuffer)) { 113 result.append(obj); 114 } else if (obj instanceof Throwable) { 115 result.append(getExceptionElement((Throwable)obj)); 116 } 117 if (m_transient) { 118 m_content.remove(m_indexNext); 119 } 120 } 121 m_indexNext = m_transient ? 0 : indexEnd; 122 return result.toString(); 123 } 124 125 /** 126 * Returns if the report writes html or javascript code.<p> 127 * 128 * @return <code>true</code> if the report writes html, and <code>false</code> if the report writes javascript code 129 */ 130 public boolean isWriteHtml() { 131 132 return m_writeHtml; 133 } 134 135 /** 136 * @see org.opencms.report.A_CmsReport#print(java.lang.String, int) 137 */ 138 @Override 139 public synchronized void print(String value, int format) { 140 141 StringBuffer buf = null; 142 143 if (!m_writeHtml) { 144 value = CmsStringUtil.escapeJavaScript(value); 145 switch (format) { 146 case FORMAT_HEADLINE: 147 buf = new StringBuffer(); 148 buf.append("aH('"); 149 buf.append(value); 150 buf.append("'); "); 151 break; 152 case FORMAT_WARNING: 153 buf = new StringBuffer(); 154 buf.append("aW('"); 155 buf.append(value); 156 buf.append("'); "); 157 addWarning(value); 158 break; 159 case FORMAT_ERROR: 160 buf = new StringBuffer(); 161 buf.append("aE('"); 162 buf.append(value); 163 buf.append("'); "); 164 addError(value); 165 break; 166 case FORMAT_NOTE: 167 buf = new StringBuffer(); 168 buf.append("aN('"); 169 buf.append(value); 170 buf.append("'); "); 171 break; 172 case FORMAT_OK: 173 buf = new StringBuffer(); 174 buf.append("aO('"); 175 buf.append(value); 176 buf.append("'); "); 177 break; 178 case FORMAT_DEFAULT: 179 default: 180 buf = new StringBuffer(); 181 buf.append("a('"); 182 buf.append(value); 183 buf.append("'); "); 184 } 185 // the output lines get split back into single lines on the client-side. 186 // thus, a separate JavaScript call has to be added here to tell the 187 // client that we want a linebreak here... 188 if (value.trim().endsWith(getLineBreak())) { 189 buf.append("aB(); "); 190 } 191 m_content.add(buf.toString()); 192 } else { 193 switch (format) { 194 case FORMAT_HEADLINE: 195 buf = new StringBuffer(); 196 buf.append("<span class='head'>"); 197 buf.append(value); 198 buf.append("</span>"); 199 break; 200 case FORMAT_WARNING: 201 buf = new StringBuffer(); 202 buf.append("<span class='warn'>"); 203 buf.append(value); 204 buf.append("</span>"); 205 addWarning(value); 206 break; 207 case FORMAT_ERROR: 208 buf = new StringBuffer(); 209 buf.append("<span class='err'>"); 210 buf.append(value); 211 buf.append("</span>"); 212 addError(value); 213 break; 214 case FORMAT_NOTE: 215 buf = new StringBuffer(); 216 buf.append("<span class='note'>"); 217 buf.append(value); 218 buf.append("</span>"); 219 break; 220 case FORMAT_OK: 221 buf = new StringBuffer(); 222 buf.append("<span class='ok'>"); 223 buf.append(value); 224 buf.append("</span>"); 225 break; 226 case FORMAT_DEFAULT: 227 default: 228 buf = new StringBuffer(value); 229 } 230 if (value.trim().endsWith(getLineBreak())) { 231 buf.append("\n"); 232 } 233 m_content.add(buf.toString()); 234 } 235 setLastEntryTime(System.currentTimeMillis()); 236 } 237 238 /** 239 * @see org.opencms.report.I_CmsReport#println() 240 */ 241 public void println() { 242 243 print(getLineBreak()); 244 } 245 246 /** 247 * @see org.opencms.report.I_CmsReport#println(java.lang.Throwable) 248 */ 249 public synchronized void println(Throwable t) { 250 251 addError(t.getMessage()); 252 m_content.add(t); 253 setLastEntryTime(System.currentTimeMillis()); 254 } 255 256 /** 257 * Returns the correct line break notation depending on the output style of this report. 258 * 259 * @return the correct line break notation 260 */ 261 protected String getLineBreak() { 262 263 return m_writeHtml ? LINEBREAK_TRADITIONAL : LINEBREAK; 264 } 265 266 /** 267 * Output helper method to format a reported {@link Throwable} element.<p> 268 * 269 * This method ensures that exception stack traces are properly escaped 270 * when they are added to the report.<p> 271 * 272 * There is a member variable {@link #m_showExceptionStackTrace} in this 273 * class that controls if the stack track is shown or not. 274 * In a later version this might be configurable on a per-user basis.<p> 275 * 276 * @param throwable the exception to format 277 * 278 * @return the formatted StringBuffer 279 */ 280 private StringBuffer getExceptionElement(Throwable throwable) { 281 282 StringBuffer buf = new StringBuffer(256); 283 284 if (!m_writeHtml) { 285 if (m_showExceptionStackTrace) { 286 buf.append("aT('"); 287 buf.append(getMessages().key(Messages.RPT_EXCEPTION_0)); 288 String exception = CmsEncoder.escapeXml(CmsException.getStackTraceAsString(throwable)); 289 StringBuffer excBuffer = new StringBuffer(exception.length() + 50); 290 StringTokenizer tok = new StringTokenizer(exception, "\r\n"); 291 while (tok.hasMoreTokens()) { 292 excBuffer.append(tok.nextToken()); 293 excBuffer.append(getLineBreak()); 294 } 295 buf.append(CmsStringUtil.escapeJavaScript(excBuffer.toString())); 296 buf.append("'); "); 297 } else { 298 buf.append("aT('"); 299 buf.append(getMessages().key(Messages.RPT_EXCEPTION_0)); 300 buf.append(CmsStringUtil.escapeJavaScript(throwable.toString())); 301 buf.append("'); "); 302 } 303 m_content.add(buf); 304 } else { 305 if (m_showExceptionStackTrace) { 306 buf.append("<span class='throw'>"); 307 buf.append(getMessages().key(Messages.RPT_EXCEPTION_0)); 308 String exception = CmsEncoder.escapeXml(CmsException.getStackTraceAsString(throwable)); 309 StringBuffer excBuffer = new StringBuffer(exception.length() + 50); 310 StringTokenizer tok = new StringTokenizer(exception, "\r\n"); 311 while (tok.hasMoreTokens()) { 312 excBuffer.append(tok.nextToken()); 313 excBuffer.append(getLineBreak()); 314 } 315 buf.append(excBuffer.toString()); 316 buf.append("</span>"); 317 } else { 318 buf.append("<span class='throw'>"); 319 buf.append(getMessages().key(Messages.RPT_EXCEPTION_0)); 320 buf.append(throwable.toString()); 321 buf.append("</span>"); 322 buf.append(getLineBreak()); 323 } 324 } 325 return buf; 326 } 327}