001package com.hfg.xml; 002 003 004import java.util.Map; 005import java.util.HashMap; 006import java.util.regex.Pattern; 007import java.util.regex.Matcher; 008 009import org.w3c.dom.NamedNodeMap; 010 011import com.hfg.util.CompareUtil; 012import com.hfg.util.StringBuilderPlus; 013import com.hfg.util.mime.MimeType; 014 015//------------------------------------------------------------------------------ 016/** 017 * Enumeration of common DOCTYPE tags. 018 * 019 * @see <a href='http://www.w3.org/QA/2002/04/valid-dtd-list.html'>W3C's recommended DTDs to use in your Web document.</a> 020 * 021 * @author J. Alex Taylor, hairyfatguy.com 022 */ 023//------------------------------------------------------------------------------ 024// com.hfg XML/HTML Coding Library 025// 026// This library is free software; you can redistribute it and/or 027// modify it under the terms of the GNU Lesser General Public 028// License as published by the Free Software Foundation; either 029// version 2.1 of the License, or (at your option) any later version. 030// 031// This library is distributed in the hope that it will be useful, 032// but WITHOUT ANY WARRANTY; without even the implied warranty of 033// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 034// Lesser General Public License for more details. 035// 036// You should have received a copy of the GNU Lesser General Public 037// License along with this library; if not, write to the Free Software 038// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 039// 040// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 041// jataylor@hairyfatguy.com 042//------------------------------------------------------------------------------ 043 044// <!DOCTYPE 045// HTML // name 046// PUBLIC 047// "-//W3C//DTD HTML 4.01//EN" // publicId 048// "http://www.w3.org/TR/html4/strict.dtd" // systemId 049// [ // internal subset in square brackets 050// <!ENTITY Zizek "Žižek"> 051// <!ENTITY Mocnik "Močnik"> 052// ]> 053 054 // DocumentType, 055public class Doctype implements Comparable<Doctype> 056{ 057 058 //########################################################################### 059 // PRIVATE FIELDS 060 //########################################################################### 061 062 private String mString; 063 private String mName; 064 private String mPublicId; 065 private String mSystemId; 066 private MimeType mMimeType; 067 068 private static Map<String, Doctype> sUniqueMap = new HashMap<String, Doctype>(10); 069 070 private static final String NL = System.getProperty("line.separator"); 071 072 //########################################################################### 073 // PUBLIC FIELDS 074 //########################################################################### 075 076 public static final Doctype HTML_4_01_STRICT 077 = new Doctype("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " 078 + "\"http://www.w3.org/TR/html4/strict.dtd\">", MimeType.TEXT_HTML); 079 080 public static final Doctype HTML_4_01_TRANSITIONAL 081 = new Doctype("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" " 082 + "\"http://www.w3.org/TR/html4/loose.dtd\">", MimeType.TEXT_HTML); 083 084 /** A workaround for IE6 */ 085 public static final Doctype HTML_4_01_TRANSITIONAL_NO_URL 086 = new Doctype("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">", MimeType.TEXT_HTML); 087 088 public static final Doctype HTML_4_01_FRAMESET 089 = new Doctype("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" " 090 + "\"http://www.w3.org/TR/html4/frameset.dtd\">", MimeType.TEXT_HTML); 091 092 public static final Doctype HTML_4_01_FRAMESET_NO_URL 093 = new Doctype("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\">", MimeType.TEXT_HTML); 094 095 public static final Doctype HTML_5 096 = new Doctype("<!DOCTYPE html>", MimeType.TEXT_HTML); 097 098 public static final Doctype XHTML_1_0_STRICT 099 = new Doctype("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " 100 + "\"http://www.w3.org/TR/xhtml1/xhtml1-strict.dtd\">", MimeType.APPLICATION_XHTML); 101 102 public static final Doctype XHTML_1_0_TRANSITIONAL 103 = new Doctype("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " 104 + "\"http://www.w3.org/TR/xhtml1/xhtml1-transitional.dtd\">", MimeType.APPLICATION_XHTML); 105 106 public static final Doctype XHTML_1_0_TRANSITIONAL_NO_URL 107 = new Doctype("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\">", MimeType.APPLICATION_XHTML); 108 109 public static final Doctype XHTML_1_0_FRAMESET 110 = new Doctype("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Frameset//EN\" " 111 + "\"http://www.w3.org/TR/xhtml1/xhtml1-frameset.dtd\">", MimeType.APPLICATION_XHTML); 112 113 public static final Doctype XHTML_1_1 114 = new Doctype("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\">", MimeType.APPLICATION_XHTML); 115 116 public static final Doctype SVG_1_1 117 = new Doctype("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">", MimeType.IMAGE_SVG_XML); 118 119 //########################################################################### 120 // CONSTRUCTORS 121 //########################################################################### 122 123 //-------------------------------------------------------------------------- 124 public Doctype() 125 { 126 127 } 128 129 130 //-------------------------------------------------------------------------- 131 private Doctype(String inValue, MimeType inMimeType) 132 { 133 mString = inValue; 134 135 if (sUniqueMap.containsKey(inValue)) 136 { 137 throw new RuntimeException("'" + inValue + "' is the value of an existing Doctype!"); 138 } 139 140 Pattern pattern = Pattern.compile("^<\\!DOCTYPE\\s+(\\S+)(?:\\s+PUBLIC\\s+([\"'])(.+?)\\2)?(?:\\s+([\"'])(.+?)\\4)?\\s*>", Pattern.CASE_INSENSITIVE); 141 Matcher m = pattern.matcher(inValue); 142 if (m.find()) 143 { 144 mName = m.group(1); 145 mPublicId = m.group(3); 146 mSystemId = m.group(5); 147 } 148 else 149 { 150 throw new XMLException(inValue + " is not a well-formed DOCTYPE!"); 151 } 152 153 sUniqueMap.put(mString, this); 154 155 setMimeType(inMimeType); 156 } 157 158 //########################################################################### 159 // PUBLIC METHODS 160 //########################################################################### 161 162 //-------------------------------------------------------------------------- 163 public static Doctype valueOf(String inValue) 164 { 165 Doctype doctype = sUniqueMap.get(inValue); 166 if (null == doctype) 167 { 168 doctype = new Doctype(inValue, null); 169 } 170 171 return doctype; 172 } 173 174 //-------------------------------------------------------------------------- 175 public Doctype setMimeType(MimeType inValue) 176 { 177 mMimeType = inValue; 178 return this; 179 } 180 181 //-------------------------------------------------------------------------- 182 public MimeType getMimeType() 183 { 184 return mMimeType; 185 } 186 187 //-------------------------------------------------------------------------- 188 public Doctype setName(String inValue) 189 { 190 mName = inValue; 191 return this; 192 } 193 194 //-------------------------------------------------------------------------- 195 public Doctype setPublicId(String inValue) 196 { 197 mPublicId = inValue; 198 return this; 199 } 200 201 //-------------------------------------------------------------------------- 202 public Doctype setSystemId(String inValue) 203 { 204 mSystemId = inValue; 205 return this; 206 } 207 208 //-------------------------------------------------------------------------- 209 @Override 210 public final String toString() 211 { 212 StringBuilderPlus buffer = new StringBuilderPlus("<!DOCTYPE").setDelimiter(" "); 213 buffer.delimitedAppend(mName); 214 if (mPublicId != null) buffer.delimitedAppend("PUBLIC \"" + mPublicId + "\""); 215 if (mSystemId != null) buffer.delimitedAppend("\"" + mSystemId + "\""); 216 buffer.append(">"); 217 218 return buffer.toString(); 219 } 220 221 //-------------------------------------------------------------------------- 222 @Override 223 public final int hashCode() 224 { 225 int value = 0; 226 if (name() != null) 227 { 228 value += 31 * name().hashCode(); 229 } 230 231 if (getPublicId() != null) 232 { 233 value += 31 * getPublicId().hashCode(); 234 } 235 236 if (getSystemId() != null) 237 { 238 value += 31 * getSystemId().hashCode(); 239 } 240 241 return value; 242 } 243 244 //-------------------------------------------------------------------------- 245 @Override 246 public final boolean equals(Object inObj) 247 { 248 return (inObj != null 249 && inObj instanceof Doctype 250 && 0 == compareTo((Doctype) inObj)); 251 } 252 253 //-------------------------------------------------------------------------- 254 public int compareTo(Doctype inObj2) 255 { 256 int result = -1; 257 if (inObj2 != null) 258 { 259 result = CompareUtil.compare(name(), inObj2.name()); 260 261 if (0 == result) 262 { 263 result = CompareUtil.compare(getPublicId(), inObj2.getPublicId()); 264 } 265 266 if (0 == result) 267 { 268 result = CompareUtil.compare(getSystemId(), inObj2.getSystemId()); 269 } 270 } 271 272 return result; 273 } 274 275 // Methods to fulfill the DocumentType interface 276 277 //-------------------------------------------------------------------------- 278 /** 279 The name of DTD; the name immediately following the DOCTYPE keyword. 280 */ 281 public String name() 282 { 283 return mName; 284 } 285 286 //-------------------------------------------------------------------------- 287 public NamedNodeMap getEntities() 288 { 289 return null; // TODO 290 } 291 292 //-------------------------------------------------------------------------- 293 public NamedNodeMap getNotations() 294 { 295 return null; // TODO 296 } 297 298 //-------------------------------------------------------------------------- 299 /** 300 The public identifier of the external subset. 301 */ 302 public String getPublicId() 303 { 304 return mPublicId; 305 } 306 307 //-------------------------------------------------------------------------- 308 /** 309 The system identifier of the external subset. 310 */ 311 public String getSystemId() 312 { 313 return mSystemId; 314 } 315 316 //-------------------------------------------------------------------------- 317 /** 318 The internal subset as a string, or null if there is none. 319 This is does not contain the delimiting square brackets. 320 The actual content returned depends on how much information is available to the implementation. 321 This may vary depending on various parameters, including the XML processor used to build the document. 322 */ 323 public String getInternalSubset() 324 { 325 return null; // TODO 326 } 327}