/*
* � Copyright IBM Corp. 2010, 2013
*
* 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 com.ibm.xsp.extlib.renderkit.html_extended.misc;
import java.io.IOException;
import java.util.IdentityHashMap;
import java.util.Iterator;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.component.UIViewRootEx;
import com.ibm.xsp.extlib.component.misc.UIDumpObject;
import com.ibm.xsp.extlib.renderkit.html_extended.FacesRendererEx;
import com.ibm.xsp.extlib.util.debug.DumpAccessor;
import com.ibm.xsp.extlib.util.debug.DumpContext;
import com.ibm.xsp.util.HtmlUtil;
import com.ibm.xsp.util.JSUtil;
/**
* Renderer the dumps a component.
*
* @author priand
*/
public class DumpObjectRenderer extends FacesRendererEx {
private final static String CATEGORY_ID_PREFIX = "c"; // $NON-NLS-1$
private final static String MAP_ID_PREFIX = "m"; // $NON-NLS-1$
private final static String ITEM_ID_PREFIX = "i"; // $NON-NLS-1$
protected static class Context extends DumpContext {
IdentityHashMap<Object, Object> stack;
// Ident level & level are different because of the categories that do not count
int indentLevel;
int level;
int maxLevel;
String filter;
int maxGridRows;
int catId;
int mapId;
int itemId;
Context(int maxLevel, String filter, int maxGridRows) {
this.stack = new IdentityHashMap<Object, Object>();
this.maxLevel = maxLevel;
this.filter = filter;
this.maxGridRows = maxGridRows;
}
}
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
if(!component.isRendered()) {
return;
}
ResponseWriter w = context.getResponseWriter();
UIDumpObject c = (UIDumpObject)component;
Object value = c.findObject(context);
String clientId = c.getClientId(context);
generateStyle(context,w);
w.startElement("div", component); // $NON-NLS-1$
w.writeAttribute("id", clientId, null); // $NON-NLS-1$
w.writeAttribute("style", "margin-top: 16px; margin-bottom: 16px; overflow-x:auto;", null); // $NON-NLS-1$ $NON-NLS-2$
newLine(w);
w.startElement("table", component); // $NON-NLS-1$
String tableSummary = "Specifies a selection of labels and their associated values displayed as tabular data."; // $NLS-DumpObjectRenderer.Specifiesaselectionoflabelsandthe-1$
w.writeAttribute("summary", tableSummary, null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTable", null); // $NON-NLS-1$ $NON-NLS-2$
newLine(w);
writeTitle(w, c);
int level = c.getLevels();
if(level==0) {
level = 999;
}
String filter = c.getStartFilter();
int maxGridRows = c.getMaxGridRows();
final boolean isUseBean = c.isUseBeanProperties();
Context dumpContext = new Context(level,filter,maxGridRows) {
@Override
public boolean shouldUseBeanProperties(Object o) {
return isUseBean;
}
};
dumpObject(w, dumpContext, null, value, clientId);
w.endElement("table"); // $NON-NLS-1$
newLine(w);
w.endElement("div"); // $NON-NLS-1$
newLine(w);
}
private void generateStyle(FacesContext context, ResponseWriter w) throws IOException {
UIViewRootEx root = (UIViewRootEx)context.getViewRoot();
if(root.getEncodeProperty(this.getClass().getName())==null) {
root.putEncodeProperty(this.getClass().getName(),Boolean.TRUE);
w.write("<style type=\"text/css\">"); // $NON-NLS-1$
w.write(".xspDumpTable {"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("border-style: solid !important;"); // $NON-NLS-1$
w.write("border-color: black !important;"); // $NON-NLS-1$
w.write("border-spacing: 0px !important;"); // $NON-NLS-1$
w.write("border-collapse: collapse !important;"); // $NON-NLS-1$
w.write("background-color: white !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTR {"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 3px !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDTitle {"); // $NON-NLS-1$
w.write("text-align: left;"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 3px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: gray !important;"); // $NON-NLS-1$
w.write("color: white !important;"); // $NON-NLS-1$
w.write("font-weight:bold !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDName {"); // $NON-NLS-1$
w.write("width: 20em !important;"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 2px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: white !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDNoValue {"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 2px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: white !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDValue {"); // $NON-NLS-1$
w.write("width: 50em !important;"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 2px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: white !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDCat1 {"); // $NON-NLS-1$
w.write("text-align: left;"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 2px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: rgb(225, 225, 225) !important;"); // $NON-NLS-1$
w.write("font-weight:bold !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDCat2 {"); // $NON-NLS-1$
w.write("text-align: left;"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 2px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: white !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpGridTable {"); // $NON-NLS-1$
w.write("margin: 0px !important;"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("border-style: solid !important;"); // $NON-NLS-1$
w.write("border-color: black !important;"); // $NON-NLS-1$
w.write("border-spacing: 0px !important;"); // $NON-NLS-1$
w.write("border-collapse: collapse !important;"); // $NON-NLS-1$
w.write("background-color: white !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDGridHeader {"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 2px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: rgb(225, 225, 225) !important;"); // $NON-NLS-1$
w.write("font-weight:bold !important;"); // $NON-NLS-1$
w.write("}");
w.write(".xspDumpTDGridValue {"); // $NON-NLS-1$
w.write("border-width: 1px !important;"); // $NON-NLS-1$
w.write("padding: 2px !important;"); // $NON-NLS-1$
w.write("border-style: outset !important;"); // $NON-NLS-1$
w.write("border-color: gray !important;"); // $NON-NLS-1$
w.write("background-color: white !important;"); // $NON-NLS-1$
w.write("}");
w.write("</style>"); // $NON-NLS-1$
newLine(w);
}
}
private void writeTitle(ResponseWriter w, UIDumpObject o) throws IOException {
String s = HtmlUtil.toHTMLContentString(o.getTitle(), false, HtmlUtil.useHTML);
if(StringUtil.isEmpty(s)) {
s = HtmlUtil.toHTMLContentString(o.getObjectNames(), false, HtmlUtil.useHTML);
}
if(StringUtil.isEmpty(s)) {
s = HtmlUtil.toHTMLContentString("Object Dump", false, HtmlUtil.useHTML); // $NLS-DumpObjectRenderer.ObjectDump-1$
}
w.startElement("tr", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTR", null); // $NON-NLS-1$ $NON-NLS-2$
w.startElement("th", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTDTitle", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("colspan", "2", null); // $NON-NLS-1$
w.write(s);
w.endElement("th"); // $NON-NLS-1$
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
}
private void dumpObject(ResponseWriter w, Context dump, String name, Object o, String clientId) throws IOException {
// If we already reached the number of levels just forget about it
if(dump.level>dump.maxLevel) {
return;
}
// If the Object had already been dumped, as part of the stack, don't do it recursively...
if(!dump.stack.containsKey(o)) {
dump.stack.put(o,Boolean.TRUE);
try {
DumpAccessor a = DumpAccessor.find(dump,o);
switch(a.getType()) {
case DumpAccessor.TYPE_VALUE: {
dumpValue(w,dump,name,(DumpAccessor.Value)a, clientId);
} break;
case DumpAccessor.TYPE_ARRAY: {
dumpArray(w,dump,name,(DumpAccessor.Array)a, clientId);
} break;
case DumpAccessor.TYPE_MAP: {
dumpMap(w,dump,name,(DumpAccessor.Map)a, clientId);
} break;
case DumpAccessor.TYPE_GRID: {
dumpGrid(w,dump,name,(DumpAccessor.Grid)a, clientId);
} break;
}
} finally {
dump.stack.remove(o);
}
}
}
private void dumpValue(ResponseWriter w, Context dump, String name, DumpAccessor.Value a, String clientId) throws IOException {
dumpValue(w, dump, name, a.getValueAsString(), a.getTypeAsString(), clientId);
}
private void dumpValue(ResponseWriter w, Context dump, String name, String value, String title, String clientId) throws IOException {
w.startElement("tr", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTR", null); // $NON-NLS-1$ $NON-NLS-2$
w.startElement("td", null); // $NON-NLS-1$
dump.itemId++;
w.writeAttribute("id", clientId + "_" + ITEM_ID_PREFIX + dump.itemId, null); // $NON-NLS-1$ $NON-NLS-2$
if (dump.catId != -1) {
w.writeAttribute("headers", clientId + "_" + CATEGORY_ID_PREFIX + dump.catId, null); // $NON-NLS-1$ $NON-NLS-2$
}
if(StringUtil.isNotEmpty(name)) {
w.writeAttribute("class", "xspDumpTDName", null); // $NON-NLS-1$ $NON-NLS-2$
if(StringUtil.isNotEmpty(title)) {
w.writeAttribute("title", title, null); // $NON-NLS-1$
}
writeIndent(w, dump.indentLevel);
if(StringUtil.isNotEmpty(name)) {
w.writeText(name,null);
} else {
w.write(" "); // $NON-NLS-1$
}
w.endElement("td"); // $NON-NLS-1$
w.startElement("td", null); // $NON-NLS-1$
if (dump.catId != -1) {
w.writeAttribute("headers", clientId + "_" + CATEGORY_ID_PREFIX + dump.catId + " " + clientId + "_" + ITEM_ID_PREFIX + dump.itemId, null); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ $NON-NLS-4$
}
else {
w.writeAttribute("headers", clientId + "_" + ITEM_ID_PREFIX + dump.itemId, null); // $NON-NLS-1$ $NON-NLS-2$
}
w.writeAttribute("class", "xspDumpTDValue", null); // $NON-NLS-1$ $NON-NLS-2$
} else {
w.writeAttribute("class", "xspDumpTDNoValue", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("colspan", "2", null); // $NON-NLS-1$
}
String s = HtmlUtil.toHTMLContentString(value, false, HtmlUtil.useHTML);
if(StringUtil.isNotEmpty(s)) {
w.write(s);
} else {
w.write(" "); // $NON-NLS-1$
}
w.endElement("td"); // $NON-NLS-1$
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
}
private void dumpArray(ResponseWriter w, Context dump, String name, DumpAccessor.Array a, String clientId) throws IOException {
w.startElement("tr", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTR", null); // $NON-NLS-1$ $NON-NLS-2$
w.startElement("td", null); // $NON-NLS-1$
if(StringUtil.isNotEmpty(name)) {
w.writeAttribute("class", "xspDumpTDName", null); // $NON-NLS-1$ $NON-NLS-2$
// if(StringUtil.isNotEmpty(title)) {
// w.writeAttribute("title", title, null); // $NON-NLS-1$
// }
writeIndent(w, dump.indentLevel);
if(StringUtil.isNotEmpty(name)) {
w.writeText(name,null);
} else {
w.write(" "); // $NON-NLS-1$
}
w.endElement("td"); // $NON-NLS-1$
w.startElement("td", null); // $NON-NLS-1$
if (dump.catId != -1) {
w.writeAttribute("headers", clientId + "_" + CATEGORY_ID_PREFIX + dump.catId + " " + clientId + "_" + ITEM_ID_PREFIX + dump.itemId, null); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ $NON-NLS-4$
}
else {
w.writeAttribute("headers", clientId + "_" + ITEM_ID_PREFIX + dump.itemId, null); // $NON-NLS-1$ $NON-NLS-2$
}
w.writeAttribute("class", "xspDumpTDValue", null); // $NON-NLS-1$ $NON-NLS-2$
} else {
w.writeAttribute("class", "xspDumpTDNoValue", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("colspan", "2", null); // $NON-NLS-1$
}
String s = HtmlUtil.toHTMLContentString("Array", false, HtmlUtil.useHTML); // $NLS-DumpObjectRenderer_HeaderForArrayArea-1$
if(StringUtil.isNotEmpty(s)) {
w.write(s);
} else {
w.write(" "); // $NON-NLS-1$
}
w.endElement("td"); // $NON-NLS-1$
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
dump.indentLevel++;
int index=0;
for(Iterator<Object> it=a.arrayIterator(); it.hasNext(); ) {
Object o = it.next();
dumpObject(w, dump, "["+index+"]", o, clientId);
index++;
}
dump.indentLevel--;
}
private void dumpArrayAsString(ResponseWriter w, Context dump, String name, DumpAccessor.Array a, String clientId) throws IOException {
// Should we layout an array in rows as well?
StringBuilder b = new StringBuilder();
b.append('[');
for(Iterator<Object> it=a.arrayIterator(); it.hasNext(); ) {
Object o = it.next();
DumpAccessor a2 = DumpAccessor.find(dump,o);
if(b.length()>1) {
b.append(',');
}
if(a2 instanceof DumpAccessor.Value) {
b.append(((DumpAccessor.Value)a2).getValueAsString());
} else {
b.append("<object>"); // $NON-NLS-1$
}
}
b.append(']');
dumpValue(w, dump, name, b.toString(), a.getTypeAsString(), clientId);
}
private void dumpMap(ResponseWriter w, Context dump, String name, DumpAccessor.Map a, String clientId) throws IOException {
String s = HtmlUtil.toHTMLContentString(name, false, HtmlUtil.useHTML);
if(s!=null) {
w.startElement("tr", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTR", null); // $NON-NLS-1$ $NON-NLS-2$
dump.mapId++;
w.startElement("td", null); // $NON-NLS-1$
w.writeAttribute("id", clientId + "_" + MAP_ID_PREFIX + dump.mapId, null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("axis", "map", null); // $NON-NLS-1$ $NON-NLS-2$
if (dump.catId != -1) {
w.writeAttribute("headers", clientId + "_" + CATEGORY_ID_PREFIX + dump.catId, null); // $NON-NLS-1$ $NON-NLS-2$
}
w.writeAttribute("class", "xspDumpTDName", null); // $NON-NLS-1$ $NON-NLS-2$
String title = a.getTypeAsString();
if(StringUtil.isNotEmpty(title)) {
w.writeAttribute("title", title, null); // $NON-NLS-1$
}
writeIndent(w,dump.indentLevel);
w.write(s);
w.endElement("td"); // $NON-NLS-1$
w.startElement("td", null); // $NON-NLS-1$
if (dump.catId != -1) {
w.writeAttribute("headers", clientId + "_" + CATEGORY_ID_PREFIX + dump.catId + " " + clientId + "_" + MAP_ID_PREFIX + dump.mapId, null); // $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$ $NON-NLS-4$
}
else {
w.writeAttribute("headers", clientId + "_" + MAP_ID_PREFIX + dump.mapId, null); // $NON-NLS-1$ $NON-NLS-2$
}
w.writeAttribute("class", "xspDumpTDValue", null); // $NON-NLS-1$ $NON-NLS-2$
String sl = HtmlUtil.toHTMLContentString(a.getStringLabel(), false, HtmlUtil.useHTML);
if(StringUtil.isNotEmpty(sl)) {
w.write(sl);
} else {
JSUtil.writeTextBlank(w);
}
w.endElement("td"); // $NON-NLS-1$
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
}
// If we already reached the number of levels just forget about it
if(dump.level<dump.maxLevel) {
String[] categories = a.getCategories();
if(categories==null || categories.length==0) {
categories = new String[1];
}
int catId = 0;
for(int i=0; i<categories.length; i++) {
String cat = categories[i];
boolean showCat = categories.length>1 || StringUtil.isNotEmpty(cat);
boolean catHandled = false;
for( Iterator<Object> it=a.getPropertyKeys(cat); it.hasNext(); ) {
Object o = it.next();
String key = o.toString();
if(dump.level==0 && StringUtil.isNotEmpty(dump.filter)) {
if(!key.startsWith(dump.filter)) {
continue;
}
}
//SPR #PHAN9E4CCA - ClassCastException in dumpObject with non-String-key'd SortedMaps
//Formerly we passed the String value, key: 'a.getProperty(key);'. Changed to pass the Object, o
Object value = a.getProperty(o);
if(!a.shouldDisplay(key, value)) {
continue;
}
if(!catHandled) {
catHandled = true;
if(showCat) {
catId++;
dump.catId = catId;
w.startElement("tr", null); // $NON-NLS-1$
w.startElement("th", null); // $NON-NLS-1$
if(dump.indentLevel>0) {
w.writeAttribute("class", "xspDumpTDCat2", null); // $NON-NLS-1$ $NON-NLS-2$
} else {
w.writeAttribute("class", "xspDumpTDCat1", null); // $NON-NLS-1$ $NON-NLS-2$
}
w.writeAttribute("id", clientId + "_" + CATEGORY_ID_PREFIX + catId, null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("axis", "category", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("colspan", "2", null); // $NON-NLS-1$
dump.indentLevel++;
writeIndent(w,dump.indentLevel);
if(StringUtil.isNotEmpty(cat)) {
w.writeText(cat,null);
} else {
JSUtil.writeTextBlank(w);
}
w.endElement("th"); // $NON-NLS-1$
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
}
else {
dump.catId = -1;
}
}
dump.indentLevel++; dump.level++;
try {
dumpObject(w,dump,key,value,clientId);
} finally {
dump.indentLevel--; dump.level--;
}
}
if(showCat) {
dump.indentLevel--;
}
dump.catId = -1;
}
}
}
private void dumpGrid(ResponseWriter w, Context dump, String name, DumpAccessor.Grid g, String clientId) throws IOException {
String[] cols = g.getColumns();
if(cols==null || cols.length==0) {
return;
}
w.startElement("tr", null); // $NON-NLS-1$
//w.writeAttribute("class", "xspDumpTR", null);
w.startElement("td", null); // $NON-NLS-1$
w.writeAttribute("style", "padding: 0px;", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeAttribute("colspan", "2", null); // $NON-NLS-1$
newLine(w);
w.startElement("table", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpGridTable", null); // $NON-NLS-1$ $NON-NLS-2$
newLine(w);
w.startElement("tr", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTR", null); // $NON-NLS-1$ $NON-NLS-2$
for(int i=0; i<cols.length; i++) {
w.startElement("td", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTDGridHeader", null); // $NON-NLS-1$ $NON-NLS-2$
w.writeText(cols[i],null);
w.endElement("td"); // $NON-NLS-1$
}
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
int max = dump.maxGridRows>0 ? dump.maxGridRows : 100;
for( Iterator<Object> it = g.objectIterator(0, max); it.hasNext(); ) {
Object o = it.next();
w.startElement("tr", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTR", null); // $NON-NLS-1$ $NON-NLS-2$
for(int i=0; i<cols.length; i++) {
w.startElement("td", null); // $NON-NLS-1$
w.writeAttribute("class", "xspDumpTDGridValue", null); // $NON-NLS-1$ $NON-NLS-2$
Object val = g.getValue(o, i);
String s = val!=null ? val.toString() : null;
if(StringUtil.isNotEmpty(s)) {
w.writeText(s,null);
} else {
JSUtil.writeTextBlank(w);
}
w.endElement("td"); // $NON-NLS-1$
}
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
}
w.endElement("table"); // $NON-NLS-1$
newLine(w);
w.endElement("td"); // $NON-NLS-1$
w.endElement("tr"); // $NON-NLS-1$
newLine(w);
}
private void writeIndent(ResponseWriter w, int indent) throws IOException {
for(int i=0; i<indent; i++) {
JSUtil.writeTextBlank(w); // $NON-NLS-1$
JSUtil.writeTextBlank(w); // $NON-NLS-1$
JSUtil.writeTextBlank(w); // $NON-NLS-1$
JSUtil.writeTextBlank(w); // $NON-NLS-1$
}
}
}