1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package org.openoffice.xmerge.converter.xml.sxc; 25 26 import org.w3c.dom.NodeList; 27 import org.w3c.dom.Node; 28 import org.w3c.dom.NamedNodeMap; 29 import org.w3c.dom.Element; 30 31 import org.openoffice.xmerge.converter.xml.Style; 32 import org.openoffice.xmerge.converter.xml.StyleCatalog; 33 import org.openoffice.xmerge.util.Debug; 34 import org.openoffice.xmerge.util.TwipsConverter; 35 36 /** 37 * Represents a text <code>Style</code> in an OpenOffice document. 38 * 39 * @author Martin Maher 40 */ 41 public class RowStyle extends Style implements Cloneable { 42 43 private int rowHeight = 255; 44 /** 45 * Constructor for use when going from DOM to client device format. 46 * 47 * @param node The <i>style:style</i> <code>Node</code> containing 48 * the <code>Style</code>. (This <code>Node</code> is 49 * assumed have a <i>family</i> attribute of <i>text</i>). 50 * @param sc The <code>StyleCatalog</code>, which is used for 51 * looking up ancestor <code>Style</code> objects. 52 */ RowStyle(Node node, StyleCatalog sc)53 public RowStyle(Node node, StyleCatalog sc) { 54 super(node, sc); 55 56 // Run through the attributes of this node, saving 57 // the ones we're interested in. 58 NamedNodeMap attrNodes = node.getAttributes(); 59 if (attrNodes != null) { 60 int len = attrNodes.getLength(); 61 for (int i = 0; i < len; i++) { 62 Node attr = attrNodes.item(i); 63 handleAttribute(attr.getNodeName(), attr.getNodeValue()); 64 } 65 } 66 67 // Look for children. Only ones we care about are "style:properties" 68 // nodes. If any are found, recursively traverse them, passing 69 // along the style element to add properties to. 70 if (node.hasChildNodes()) { 71 NodeList children = node.getChildNodes(); 72 int len = children.getLength(); 73 for (int i = 0; i < len; i++) { 74 Node child = children.item(i); 75 String name = child.getNodeName(); 76 if (name.equals("style:properties")) { 77 NamedNodeMap childAttrNodes = child.getAttributes(); 78 if (childAttrNodes != null) { 79 int nChildAttrNodes = childAttrNodes.getLength(); 80 for (int j = 0; j < nChildAttrNodes; j++) { 81 Node attr = childAttrNodes.item(j); 82 handleAttribute(attr.getNodeName(), 83 attr.getNodeValue()); 84 } 85 } 86 } 87 } 88 } 89 } 90 91 92 /** 93 * Constructor for use when going from client device format to DOM 94 * 95 * @param name Name of text <code>Style</code>. Can be null. 96 * @param family Family of text <code>Style</code> (usually 97 * <i>text</i>). Can be null. 98 * @param parent Name of parent text <code>Style</code>, or null 99 * for none. 100 * @param rowHeight The height of this row 101 * @param sc The <code>StyleCatalog</code>, which is used for 102 * looking up ancestor <code>Style</code> objects. 103 */ RowStyle(String name, String family, String parent,int rowHeight, StyleCatalog sc)104 public RowStyle(String name, String family, String parent,int rowHeight, StyleCatalog sc) { 105 super(name, family, parent, sc); 106 this.rowHeight=rowHeight; 107 } 108 109 /** 110 * Returns the height of this row 111 * 112 * @return the <code>Format</code> object 113 */ getRowHeight()114 public int getRowHeight() { 115 return rowHeight; 116 } 117 118 /** 119 * Sets the height of this row 120 * 121 * @param rowHeight the row height 122 */ setRowHeight(int rowHeight)123 public void setRowHeight(int rowHeight) { 124 125 this.rowHeight = rowHeight; 126 } 127 /** 128 * Parse a colheight in the form "1.234cm" to twips 129 * 130 * @param value <code>String</code> specification to parse. 131 * 132 * @return The twips equivalent. 133 */ parseRowHeight(String value)134 private int parseRowHeight(String value) { 135 136 int height = 255; // Default value 137 138 if(value.indexOf("cm")!=-1) { 139 float heightCM = Float.parseFloat(value.substring(0,value.indexOf("c"))); 140 height = TwipsConverter.cm2twips(heightCM); 141 } else if(value.indexOf("inch")!=-1) { 142 float heightInch = Float.parseFloat(value.substring(0,value.indexOf("i"))); 143 height = TwipsConverter.inches2twips(heightInch); 144 } 145 146 return (height); 147 148 } 149 150 151 /** 152 * Set an attribute. 153 * 154 * @param attr The attribute to set. 155 * @param value The attribute value to set. 156 */ handleAttribute(String attr, String value)157 private void handleAttribute(String attr, String value) { 158 159 if (attr.equals("style:row-height")) { 160 rowHeight = parseRowHeight(value); 161 } 162 else { 163 Debug.log(Debug.INFO, "RowStyle Unhandled: " + attr + "=" + value); 164 } 165 } 166 167 168 /** 169 * Return a <code>Style</code> object corresponding to this one, 170 * but with all of the inherited information from parent 171 * <code>Style</code> objects filled in. The object returned will 172 * be a new object, not a reference to this object, even if it does 173 * not need any information added. 174 * 175 * @return The <code>StyleCatalog</code> in which to look up 176 * ancestors. 177 */ getResolved()178 public Style getResolved() { 179 // Create a new object to return, which is a clone of this one. 180 RowStyle resolved = null; 181 try { 182 resolved = (RowStyle)this.clone(); 183 } catch (Exception e) { 184 Debug.log(Debug.ERROR, "Can't clone", e); 185 } 186 187 // Look up the parentStyle. (If there is no style catalog 188 // specified, we can't do any lookups.) 189 RowStyle parentStyle = null; 190 if (sc != null) { 191 if (parent != null) { 192 parentStyle = (RowStyle)sc.lookup(parent, family, null, 193 this.getClass()); 194 if (parentStyle == null) 195 Debug.log(Debug.ERROR, "parent style lookup of " 196 + parent + " failed!"); 197 else 198 parentStyle = (RowStyle)parentStyle.getResolved(); 199 200 } else if (!name.equals("DEFAULT_STYLE")) { 201 parentStyle = (RowStyle)sc.lookup("DEFAULT_STYLE", null, 202 null, this.getClass()); 203 } 204 } 205 206 // If we found a parent, for any attributes which we don't have 207 // set, try to get the values from the parent. 208 if (parentStyle != null) { 209 parentStyle = (RowStyle)parentStyle.getResolved(); 210 211 if ((rowHeight == 0) && (parentStyle.getRowHeight() != 0)) 212 resolved.setRowHeight(parentStyle.getRowHeight()); 213 } 214 return resolved; 215 } 216 217 218 /** 219 * Create a new <code>Node</code> in the <code>Document</code>, and 220 * write this <code>Style</code> to it. 221 * 222 * @param parentDoc Parent <code>Document</code> of the 223 * <code>Node</code> to create. 224 * @param name Name to use for the new <code>Node</code> (e.g. 225 * <i>style:style</i>) 226 * 227 * @return Created <code>Node</code>. 228 */ createNode(org.w3c.dom.Document parentDoc, String name)229 public Node createNode(org.w3c.dom.Document parentDoc, String name) { 230 Element node = parentDoc.createElement(name); 231 writeAttributes(node); 232 return node; 233 } 234 235 236 /** 237 * Return true if <code>style</code> specifies as much or less 238 * than this <code>Style</code>, and nothing it specifies 239 * contradicts this <code>Style</code>. 240 * 241 * @param style The <code>Style</code> to check. 242 * 243 * @return true if <code>style</code> is a subset, false 244 * otherwise. 245 */ isSubset(Style style)246 public boolean isSubset(Style style) { 247 if (style.getClass() != this.getClass()) 248 return false; 249 RowStyle tStyle = (RowStyle)style; 250 251 if(rowHeight!=tStyle.getRowHeight()) 252 return false; 253 254 return true; 255 } 256 257 258 /** 259 * Write this <code>Style</code> object's attributes to a 260 * <code>Node</code> in the <code>Document</code>. 261 * 262 * @param node The <code>Node</code> to add <code>Style</code> 263 * attributes. 264 */ writeAttributes(Element node)265 public void writeAttributes(Element node) { 266 267 if(rowHeight!=0) { 268 String height = TwipsConverter.twips2cm(rowHeight) + "cm"; 269 node.setAttribute("style:row-height", height); 270 } 271 } 272 273 274 private static String[] ignored = { 275 "fo:break-before", "fo:keep-with-next" 276 }; 277 278 279 /* 280 * This code checks whether an attribute is one that we 281 * intentionally ignore. 282 * 283 * @param attribute The attribute to check. 284 * 285 * @return true if <code>attribute</code> can be ignored, 286 * otherwise false. 287 */ isIgnored(String attribute)288 private boolean isIgnored(String attribute) { 289 for (int i = 0; i < ignored.length; i++) { 290 if (ignored[i].equals(attribute)) 291 return true; 292 } 293 return false; 294 } 295 } 296 297