1 31 32 package org.opencms.xml.content; 33 34 import org.opencms.file.CmsFile; 35 import org.opencms.file.CmsObject; 36 import org.opencms.file.CmsResource; 37 import org.opencms.i18n.CmsEncoder; 38 import org.opencms.i18n.CmsLocaleManager; 39 import org.opencms.main.CmsIllegalArgumentException; 40 import org.opencms.main.CmsLog; 41 import org.opencms.main.CmsRuntimeException; 42 import org.opencms.staticexport.CmsLinkProcessor; 43 import org.opencms.staticexport.CmsLinkTable; 44 import org.opencms.xml.A_CmsXmlDocument; 45 import org.opencms.xml.CmsXmlContentDefinition; 46 import org.opencms.xml.CmsXmlException; 47 import org.opencms.xml.CmsXmlUtils; 48 import org.opencms.xml.I_CmsXmlDocument; 49 import org.opencms.xml.types.CmsXmlNestedContentDefinition; 50 import org.opencms.xml.types.I_CmsXmlContentValue; 51 import org.opencms.xml.types.I_CmsXmlSchemaType; 52 53 import java.io.IOException ; 54 import java.util.ArrayList ; 55 import java.util.Collections ; 56 import java.util.HashMap ; 57 import java.util.HashSet ; 58 import java.util.Iterator ; 59 import java.util.List ; 60 import java.util.Locale ; 61 import java.util.Set ; 62 63 import org.apache.commons.logging.Log; 64 65 import org.dom4j.Document; 66 import org.dom4j.Element; 67 import org.dom4j.Node; 68 import org.xml.sax.EntityResolver ; 69 import org.xml.sax.SAXException ; 70 71 84 public class CmsXmlContent extends A_CmsXmlDocument implements I_CmsXmlDocument { 85 86 87 public static final String XERCES_SCHEMA_PROPERTY = "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation"; 88 89 90 private static final Log LOG = CmsLog.getLog(CmsXmlContent.class); 91 92 93 protected CmsXmlContentDefinition m_contentDefinition; 94 95 98 protected CmsXmlContent() { 99 100 } 102 103 114 protected CmsXmlContent(CmsObject cms, Locale locale, String encoding, CmsXmlContentDefinition contentDefinition) { 115 116 m_contentDefinition = contentDefinition; 118 Document document = contentDefinition.createDocument(cms, this, locale); 120 initDocument(document, encoding, m_contentDefinition); 122 } 123 124 133 protected CmsXmlContent(Document document, String encoding, EntityResolver resolver) { 134 135 m_document = document; 137 m_contentDefinition = getContentDefinition(resolver); 139 initDocument(m_document, encoding, m_contentDefinition); 141 } 142 143 146 public void addLocale(CmsObject cms, Locale locale) throws CmsXmlException { 147 148 if (hasLocale(locale)) { 149 throw new CmsXmlException(org.opencms.xml.page.Messages.get().container( 150 org.opencms.xml.page.Messages.ERR_XML_PAGE_LOCALE_EXISTS_1, 151 locale)); 152 } 153 m_contentDefinition.createLocale(cms, this, m_document.getRootElement(), locale); 155 initDocument(m_document, m_encoding, m_contentDefinition); 157 } 158 159 174 public I_CmsXmlContentValue addValue(CmsObject cms, String path, Locale locale, int index) 175 throws CmsIllegalArgumentException, CmsRuntimeException { 176 177 I_CmsXmlSchemaType type = m_contentDefinition.getSchemaType(path); 179 if (type == null) { 180 throw new CmsIllegalArgumentException(Messages.get().container( 181 Messages.ERR_XMLCONTENT_UNKNOWN_ELEM_PATH_SCHEMA_1, 182 path)); 183 } 184 185 Element parentElement; 186 String elementName; 187 CmsXmlContentDefinition contentDefinition; 188 if (CmsXmlUtils.isDeepXpath(path)) { 189 String parentPath = CmsXmlUtils.removeLastXpathElement(path); 191 Object o = getBookmark(parentPath, locale); 192 if (o == null) { 193 throw new CmsIllegalArgumentException(Messages.get().container( 194 Messages.ERR_XMLCONTENT_UNKNOWN_ELEM_PATH_1, 195 path)); 196 } 197 CmsXmlNestedContentDefinition parentValue = (CmsXmlNestedContentDefinition)o; 198 parentElement = parentValue.getElement(); 199 elementName = CmsXmlUtils.getLastXpathElement(path); 200 contentDefinition = parentValue.getNestedContentDefinition(); 201 } else { 202 parentElement = getLocaleNode(locale); 204 elementName = CmsXmlUtils.removeXpathIndex(path); 205 contentDefinition = m_contentDefinition; 206 } 207 208 List siblings = parentElement.elements(elementName); 210 211 int insertIndex; 212 if (siblings.size() > 0) { 213 214 if (siblings.size() >= type.getMaxOccurs()) { 215 throw new CmsRuntimeException(Messages.get().container( 217 Messages.ERR_XMLCONTENT_ELEM_MAXOCCURS_2, 218 elementName, 219 new Integer (type.getMaxOccurs()))); 220 } 221 222 if (index > siblings.size()) { 223 throw new CmsRuntimeException(Messages.get().container( 225 Messages.ERR_XMLCONTENT_ADD_ELEM_INVALID_IDX_3, 226 new Integer (index), 227 new Integer (siblings.size()))); 228 } 229 230 int offset = (index == siblings.size()) ? 1 : 0; 232 Element sibling = (Element)siblings.get(index - offset); 234 insertIndex = sibling.getParent().content().indexOf(sibling) + offset; 236 } else { 237 238 if (index > 0) { 239 throw new CmsRuntimeException(Messages.get().container( 241 Messages.ERR_XMLCONTENT_ADD_ELEM_INVALID_IDX_2, 242 new Integer (index), 243 elementName)); 244 } 245 246 int typeIndex = contentDefinition.getTypeSequence().indexOf(type); 248 if (typeIndex == 0) { 249 insertIndex = 0; 251 } else { 252 253 List previousTypeNames = new ArrayList (); 255 for (int i = 0; i < typeIndex; i++) { 256 I_CmsXmlSchemaType t = (I_CmsXmlSchemaType)contentDefinition.getTypeSequence().get(i); 257 previousTypeNames.add(t.getName()); 258 } 259 260 Iterator i = parentElement.content().iterator(); 262 int pos = 0; 263 while (i.hasNext()) { 264 Node node = (Node)i.next(); 265 if (node instanceof Element) { 266 if (!previousTypeNames.contains(node.getName())) { 267 break; 270 } 271 } 272 pos++; 273 } 274 insertIndex = pos; 275 } 276 } 277 278 I_CmsXmlContentValue newValue = addValue(cms, parentElement, type, locale, insertIndex); 280 281 initDocument(m_document, m_encoding, m_contentDefinition); 283 284 return (I_CmsXmlContentValue)getBookmark(getBookmarkName(newValue.getPath(), locale)); 287 } 288 289 292 public CmsXmlContentDefinition getContentDefinition() { 293 294 return m_contentDefinition; 295 } 296 297 300 public CmsLinkProcessor getLinkProcessor(CmsObject cms, CmsLinkTable linkTable) { 301 302 String relativeRoot = null; 304 if (m_file != null) { 305 relativeRoot = CmsResource.getParentFolder(cms.getSitePath(m_file)); 306 } 307 return new CmsLinkProcessor(cms, linkTable, getEncoding(), relativeRoot); 308 } 309 310 321 public CmsXmlContentValueSequence getValueSequence(String name, Locale locale) { 322 323 I_CmsXmlSchemaType type = m_contentDefinition.getSchemaType(name); 324 if (type == null) { 325 return null; 326 } 327 return new CmsXmlContentValueSequence(name, type, locale, this); 328 } 329 330 338 public void removeValue(String name, Locale locale, int index) { 339 340 I_CmsXmlContentValue value = getValue(name, locale, index); 342 343 List values = getValues(name, locale); 345 if (values.size() <= value.getMinOccurs()) { 346 throw new CmsRuntimeException(Messages.get().container( 348 Messages.ERR_XMLCONTENT_ELEM_MINOCCURS_2, 349 name, 350 new Integer (value.getMinOccurs()))); 351 } 352 353 value.getElement().detach(); 355 356 initDocument(m_document, m_encoding, m_contentDefinition); 358 359 } 360 361 366 public void resolveMappings(CmsObject cms) { 367 368 CmsXmlContentMappingVisitor visitor = new CmsXmlContentMappingVisitor(cms, this); 370 visitAllValuesWith(visitor); 371 } 372 373 376 public CmsXmlContentErrorHandler validate(CmsObject cms) { 377 378 CmsXmlContentValidationVisitor visitor = new CmsXmlContentValidationVisitor(cms); 380 visitAllValuesWith(visitor); 381 382 return visitor.getErrorHandler(); 383 } 384 385 394 public void visitAllValuesWith(I_CmsXmlContentValueVisitor visitor) { 395 396 List bookmarks = new ArrayList (getBookmarks()); 397 Collections.sort(bookmarks); 398 399 for (int i = 0; i < bookmarks.size(); i++) { 400 401 String key = (String )bookmarks.get(i); 402 I_CmsXmlContentValue value = (I_CmsXmlContentValue)getBookmark(key); 403 visitor.visit(value); 404 } 405 } 406 407 410 protected Object getBookmark(String bookmark) { 411 412 return super.getBookmark(bookmark); 414 } 415 416 419 protected Set getBookmarks() { 420 421 return super.getBookmarks(); 423 } 424 425 434 protected Element getLocaleNode(Locale locale) throws CmsRuntimeException { 435 436 String localeStr = locale.toString(); 437 Iterator i = m_document.getRootElement().elements().iterator(); 438 while (i.hasNext()) { 439 Element element = (Element)i.next(); 440 if (localeStr.equals(element.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE))) { 441 return element; 443 } 444 } 445 446 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_XMLCONTENT_MISSING_LOCALE_1, locale)); 448 } 449 450 453 protected void initDocument(Document document, String encoding, CmsXmlContentDefinition definition) { 454 455 m_document = document; 456 m_contentDefinition = definition; 457 m_encoding = CmsEncoder.lookupEncoding(encoding, encoding); 458 m_elementLocales = new HashMap (); 459 m_elementNames = new HashMap (); 460 m_locales = new HashSet (); 461 clearBookmarks(); 462 463 for (Iterator i = m_document.getRootElement().elementIterator(); i.hasNext();) { 465 Element node = (Element)i.next(); 466 try { 467 Locale locale = CmsLocaleManager.getLocale(node.attribute( 468 CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE).getValue()); 469 470 addLocale(locale); 471 processSchemaNode(node, null, locale, definition); 472 } catch (NullPointerException e) { 473 LOG.error(Messages.get().getBundle().key(Messages.LOG_XMLCONTENT_INIT_BOOKMARKS_0), e); 474 } 475 } 476 477 } 478 479 484 protected void setFile(CmsFile file) { 485 486 m_file = file; 487 } 488 489 500 private I_CmsXmlContentValue addValue( 501 CmsObject cms, 502 Element parent, 503 I_CmsXmlSchemaType type, 504 Locale locale, 505 int insertIndex) { 506 507 Element element = type.generateXml(cms, this, parent, locale); 509 element.detach(); 511 parent.content().add(insertIndex, element); 513 I_CmsXmlContentValue value = type.createValue(this, element, locale); 515 String defaultValue = m_contentDefinition.getContentHandler().getDefault(cms, value, locale); 517 if (defaultValue != null) { 518 value.setStringValue(cms, defaultValue); 520 } 521 return value; 523 } 524 525 536 private CmsXmlContentDefinition getContentDefinition(EntityResolver resolver) throws CmsRuntimeException { 537 538 String schemaLocation = m_document.getRootElement().attributeValue( 539 I_CmsXmlSchemaType.XSI_NAMESPACE_ATTRIBUTE_NO_SCHEMA_LOCATION); 540 if (schemaLocation == null) { 545 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_XMLCONTENT_MISSING_SCHEMA_0)); 546 } 547 548 try { 549 return CmsXmlContentDefinition.unmarshal(schemaLocation, resolver); 550 } catch (SAXException e) { 551 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_XML_SCHEMA_PARSE_0), e); 552 } catch (IOException e) { 553 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_XML_SCHEMA_IO_0), e); 554 } catch (CmsXmlException e) { 555 throw new CmsRuntimeException(Messages.get().container(Messages.ERR_XMLCONTENT_UNMARSHAL_0), e); 556 } 557 } 558 559 568 private void processSchemaNode(Element root, String rootPath, Locale locale, CmsXmlContentDefinition definition) { 569 570 int count = 1; 571 String previousName = null; 572 573 List content = root.content(); 575 for (int i = content.size() - 1; i >= 0; i--) { 576 Node node = (Node)content.get(i); 577 if (!(node instanceof Element)) { 578 content.remove(i); 580 } 581 } 582 583 for (Iterator i = root.content().iterator(); i.hasNext();) { 585 586 Element element = (Element)i.next(); 588 589 String name = element.getName(); 591 if ((previousName == null) || !previousName.equals(name)) { 592 previousName = name; 593 count = 1; 594 } 595 596 String path; 598 if (rootPath != null) { 599 StringBuffer b = new StringBuffer (rootPath.length() + name.length() + 6); 600 b.append(rootPath); 601 b.append('/'); 602 b.append(CmsXmlUtils.createXpathElement(name, count)); 603 path = b.toString(); 604 } else { 605 path = CmsXmlUtils.createXpathElement(name, count); 606 } 607 608 I_CmsXmlSchemaType schemaType = definition.getSchemaType(name); 610 611 if (schemaType != null) { 612 I_CmsXmlContentValue value = schemaType.createValue(this, element, locale); 614 addBookmark(path, locale, true, value); 615 616 if (!schemaType.isSimpleType()) { 617 CmsXmlNestedContentDefinition nestedSchema = (CmsXmlNestedContentDefinition)schemaType; 619 processSchemaNode(element, path, locale, nestedSchema.getNestedContentDefinition()); 620 } 621 } else { 622 if (LOG.isWarnEnabled()) { 624 LOG.warn(Messages.get().getBundle().key( 625 Messages.LOG_XMLCONTENT_INVALID_ELEM_2, 626 name, 627 definition.getSchemaLocation())); 628 } 629 } 630 631 count++; 633 } 634 } 635 } | Popular Tags |