[bug-62522] include partName in exception message

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1835276 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2018-07-06 19:07:22 +00:00
parent 0c3e972a71
commit 75d1771f49

View File

@ -43,433 +43,434 @@ import org.xml.sax.SAXException;
*/ */
public abstract class ContentTypeManager { public abstract class ContentTypeManager {
/** /**
* Content type part name. * Content type part name.
*/ */
public static final String CONTENT_TYPES_PART_NAME = "[Content_Types].xml"; public static final String CONTENT_TYPES_PART_NAME = "[Content_Types].xml";
/** /**
* Content type namespace * Content type namespace
*/ */
public static final String TYPES_NAMESPACE_URI = PackageNamespaces.CONTENT_TYPES; public static final String TYPES_NAMESPACE_URI = PackageNamespaces.CONTENT_TYPES;
/* Xml elements in content type part */ /* Xml elements in content type part */
private static final String TYPES_TAG_NAME = "Types"; private static final String TYPES_TAG_NAME = "Types";
private static final String DEFAULT_TAG_NAME = "Default"; private static final String DEFAULT_TAG_NAME = "Default";
private static final String EXTENSION_ATTRIBUTE_NAME = "Extension"; private static final String EXTENSION_ATTRIBUTE_NAME = "Extension";
private static final String CONTENT_TYPE_ATTRIBUTE_NAME = "ContentType"; private static final String CONTENT_TYPE_ATTRIBUTE_NAME = "ContentType";
private static final String OVERRIDE_TAG_NAME = "Override"; private static final String OVERRIDE_TAG_NAME = "Override";
private static final String PART_NAME_ATTRIBUTE_NAME = "PartName"; private static final String PART_NAME_ATTRIBUTE_NAME = "PartName";
/** /**
* Reference to the package using this content type manager. * Reference to the package using this content type manager.
*/ */
protected OPCPackage container; protected OPCPackage container;
/** /**
* Default content type tree. <Extension, ContentType> * Default content type tree. <Extension, ContentType>
*/ */
private TreeMap<String, String> defaultContentType; private TreeMap<String, String> defaultContentType;
/** /**
* Override content type tree. * Override content type tree.
*/ */
private TreeMap<PackagePartName, String> overrideContentType; private TreeMap<PackagePartName, String> overrideContentType;
/** /**
* Constructor. Parses the content of the specified input stream. * Constructor. Parses the content of the specified input stream.
* *
* @param in * @param in
* If different of <i>null</i> then the content types part is * If different of <i>null</i> then the content types part is
* retrieve and parse. * retrieve and parse.
* @throws InvalidFormatException * @throws InvalidFormatException
* If the content types part content is not valid. * If the content types part content is not valid.
*/ */
public ContentTypeManager(InputStream in, OPCPackage pkg) public ContentTypeManager(InputStream in, OPCPackage pkg)
throws InvalidFormatException { throws InvalidFormatException {
this.container = pkg; this.container = pkg;
this.defaultContentType = new TreeMap<>(); this.defaultContentType = new TreeMap<>();
if (in != null) { if (in != null) {
try { try {
parseContentTypesFile(in); parseContentTypesFile(in);
} catch (InvalidFormatException e) { } catch (InvalidFormatException e) {
InvalidFormatException ex = new InvalidFormatException("Can't read content types part !"); InvalidFormatException ex = new InvalidFormatException("Can't read content types part !");
// here it is useful to add the cause to not loose the original stack-trace // here it is useful to add the cause to not loose the original stack-trace
ex.initCause(e); ex.initCause(e);
throw ex; throw ex;
} }
} }
} }
/** /**
* Build association extention-> content type (will be stored in * Build association extention-> content type (will be stored in
* [Content_Types].xml) for example ContentType="image/png" Extension="png" * [Content_Types].xml) for example ContentType="image/png" Extension="png"
* <p>
* [M2.8]: When adding a new part to a package, the package implementer
* shall ensure that a content type for that part is specified in the
* Content Types stream; the package implementer shall perform the steps
* described in &#167;9.1.2.3:
* </p><p>
* 1. Get the extension from the part name by taking the substring to the
* right of the rightmost occurrence of the dot character (.) from the
* rightmost segment.
* </p><p>
* 2. If a part name has no extension, a corresponding Override element
* shall be added to the Content Types stream.
* </p><p>
* 3. Compare the resulting extension with the values specified for the
* Extension attributes of the Default elements in the Content Types stream.
* The comparison shall be case-insensitive ASCII.
* </p><p>
* 4. If there is a Default element with a matching Extension attribute,
* then the content type of the new part shall be compared with the value of
* the ContentType attribute. The comparison might be case-sensitive and
* include every character regardless of the role it plays in the
* content-type grammar of RFC 2616, or it might follow the grammar of RFC
* 2616.
* </p><p>
* a. If the content types match, no further action is required.
* </p><p>
* b. If the content types do not match, a new Override element shall be
* added to the Content Types stream. .
* </p><p>
* 5. If there is no Default element with a matching Extension attribute, a
* new Default element or Override element shall be added to the Content
* Types stream.
* </p>
*/
public void addContentType(PackagePartName partName, String contentType) {
boolean defaultCTExists = this.defaultContentType.containsValue(contentType);
String extension = partName.getExtension().toLowerCase(Locale.ROOT);
if ((extension.length() == 0)
|| (this.defaultContentType.containsKey(extension) && !defaultCTExists))
this.addOverrideContentType(partName, contentType);
else if (!defaultCTExists)
this.addDefaultContentType(extension, contentType);
}
/**
* Add an override content type for a specific part.
*
* @param partName
* Name of the part.
* @param contentType
* Content type of the part.
*/
private void addOverrideContentType(PackagePartName partName,
String contentType) {
if (overrideContentType == null)
overrideContentType = new TreeMap<>();
overrideContentType.put(partName, contentType);
}
/**
* Add a content type associated with the specified extension.
*
* @param extension
* The part name extension to bind to a content type.
* @param contentType
* The content type associated with the specified extension.
*/
private void addDefaultContentType(String extension, String contentType) {
// Remark : Originally the latest parameter was :
// contentType.toLowerCase(). Change due to a request ID 1996748.
defaultContentType.put(extension.toLowerCase(Locale.ROOT), contentType);
}
/**
* <p> * <p>
* Delete a content type based on the specified part name. If the specified * [M2.8]: When adding a new part to a package, the package implementer
* part name is register with an override content type, then this content * shall ensure that a content type for that part is specified in the
* type is remove, else the content type is remove in the default content * Content Types stream; the package implementer shall perform the steps
* type list if it exists and if no part is associated with it yet. * described in &#167;9.1.2.3:
* </p><p> * </p><p>
* Check rule M2.4: The package implementer shall require that the Content * 1. Get the extension from the part name by taking the substring to the
* Types stream contain one of the following for every part in the package: * right of the rightmost occurrence of the dot character (.) from the
* One matching Default element One matching Override element Both a * rightmost segment.
* matching Default element and a matching Override element, in which case * </p><p>
* the Override element takes precedence. * 2. If a part name has no extension, a corresponding Override element
* </p> * shall be added to the Content Types stream.
* @param partName * </p><p>
* The part URI associated with the override content type to * 3. Compare the resulting extension with the values specified for the
* delete. * Extension attributes of the Default elements in the Content Types stream.
* @exception InvalidOperationException * The comparison shall be case-insensitive ASCII.
* Throws if * </p><p>
*/ * 4. If there is a Default element with a matching Extension attribute,
public void removeContentType(PackagePartName partName) * then the content type of the new part shall be compared with the value of
throws InvalidOperationException { * the ContentType attribute. The comparison might be case-sensitive and
if (partName == null) * include every character regardless of the role it plays in the
throw new IllegalArgumentException("partName"); * content-type grammar of RFC 2616, or it might follow the grammar of RFC
* 2616.
* </p><p>
* a. If the content types match, no further action is required.
* </p><p>
* b. If the content types do not match, a new Override element shall be
* added to the Content Types stream. .
* </p><p>
* 5. If there is no Default element with a matching Extension attribute, a
* new Default element or Override element shall be added to the Content
* Types stream.
* </p>
*/
public void addContentType(PackagePartName partName, String contentType) {
boolean defaultCTExists = this.defaultContentType.containsValue(contentType);
String extension = partName.getExtension().toLowerCase(Locale.ROOT);
if ((extension.length() == 0)
|| (this.defaultContentType.containsKey(extension) && !defaultCTExists))
this.addOverrideContentType(partName, contentType);
else if (!defaultCTExists)
this.addDefaultContentType(extension, contentType);
}
/* Override content type */ /**
if (this.overrideContentType != null * Add an override content type for a specific part.
&& (this.overrideContentType.get(partName) != null)) { *
// Remove the override definition for the specified part. * @param partName
this.overrideContentType.remove(partName); * Name of the part.
return; * @param contentType
} * Content type of the part.
*/
private void addOverrideContentType(PackagePartName partName,
String contentType) {
if (overrideContentType == null)
overrideContentType = new TreeMap<>();
overrideContentType.put(partName, contentType);
}
/* Default content type */ /**
String extensionToDelete = partName.getExtension(); * Add a content type associated with the specified extension.
boolean deleteDefaultContentTypeFlag = true; *
if (this.container != null) { * @param extension
try { * The part name extension to bind to a content type.
for (PackagePart part : this.container.getParts()) { * @param contentType
if (!part.getPartName().equals(partName) * The content type associated with the specified extension.
&& part.getPartName().getExtension() */
.equalsIgnoreCase(extensionToDelete)) { private void addDefaultContentType(String extension, String contentType) {
deleteDefaultContentTypeFlag = false; // Remark : Originally the latest parameter was :
break; // contentType.toLowerCase(). Change due to a request ID 1996748.
} defaultContentType.put(extension.toLowerCase(Locale.ROOT), contentType);
} }
} catch (InvalidFormatException e) {
throw new InvalidOperationException(e.getMessage());
}
}
// Remove the default content type, no other part use this content type. /**
if (deleteDefaultContentTypeFlag) { * <p>
this.defaultContentType.remove(extensionToDelete); * Delete a content type based on the specified part name. If the specified
} * part name is register with an override content type, then this content
* type is remove, else the content type is remove in the default content
* type list if it exists and if no part is associated with it yet.
* </p><p>
* Check rule M2.4: The package implementer shall require that the Content
* Types stream contain one of the following for every part in the package:
* One matching Default element One matching Override element Both a
* matching Default element and a matching Override element, in which case
* the Override element takes precedence.
* </p>
* @param partName
* The part URI associated with the override content type to
* delete.
* @exception InvalidOperationException
* Throws if
*/
public void removeContentType(PackagePartName partName)
throws InvalidOperationException {
if (partName == null)
throw new IllegalArgumentException("partName");
/* /* Override content type */
* Check rule 2.4: The package implementer shall require that the if (this.overrideContentType != null
* Content Types stream contain one of the following for every part in && (this.overrideContentType.get(partName) != null)) {
* the package: One matching Default element One matching Override // Remove the override definition for the specified part.
* element Both a matching Default element and a matching Override this.overrideContentType.remove(partName);
* element, in which case the Override element takes precedence. return;
*/ }
if (this.container != null) {
try {
for (PackagePart part : this.container.getParts()) {
if (!part.getPartName().equals(partName)
&& this.getContentType(part.getPartName()) == null)
throw new InvalidOperationException(
"Rule M2.4 is not respected: Nor a default element or override element is associated with the part: "
+ part.getPartName().getName());
}
} catch (InvalidFormatException e) {
throw new InvalidOperationException(e.getMessage());
}
}
}
/** /* Default content type */
* Check if the specified content type is already register. String extensionToDelete = partName.getExtension();
* boolean deleteDefaultContentTypeFlag = true;
* @param contentType if (this.container != null) {
* The content type to check. try {
* @return <code>true</code> if the specified content type is already for (PackagePart part : this.container.getParts()) {
* register, then <code>false</code>. if (!part.getPartName().equals(partName)
*/ && part.getPartName().getExtension()
public boolean isContentTypeRegister(String contentType) { .equalsIgnoreCase(extensionToDelete)) {
if (contentType == null) deleteDefaultContentTypeFlag = false;
throw new IllegalArgumentException("contentType"); break;
}
}
} catch (InvalidFormatException e) {
throw new InvalidOperationException(e.getMessage());
}
}
return (this.defaultContentType.values().contains(contentType) || (this.overrideContentType != null && this.overrideContentType // Remove the default content type, no other part use this content type.
.values().contains(contentType))); if (deleteDefaultContentTypeFlag) {
} this.defaultContentType.remove(extensionToDelete);
}
/** /*
* Get the content type for the specified part, if any. * Check rule 2.4: The package implementer shall require that the
* <p> * Content Types stream contain one of the following for every part in
* Rule [M2.9]: To get the content type of a part, the package implementer * the package: One matching Default element One matching Override
* shall perform the steps described in &#167;9.1.2.4: * element Both a matching Default element and a matching Override
* </p><p> * element, in which case the Override element takes precedence.
* 1. Compare the part name with the values specified for the PartName */
* attribute of the Override elements. The comparison shall be if (this.container != null) {
* case-insensitive ASCII. try {
* </p><p> for (PackagePart part : this.container.getParts()) {
* 2. If there is an Override element with a matching PartName attribute, if (!part.getPartName().equals(partName)
* return the value of its ContentType attribute. No further action is && this.getContentType(part.getPartName()) == null)
* required. throw new InvalidOperationException(
* </p><p> "Rule M2.4 is not respected: Nor a default element or override element is associated with the part: "
* 3. If there is no Override element with a matching PartName attribute, + part.getPartName().getName());
* then a. Get the extension from the part name by taking the substring to }
* the right of the rightmost occurrence of the dot character (.) from the } catch (InvalidFormatException e) {
* rightmost segment. b. Check the Default elements of the Content Types throw new InvalidOperationException(e.getMessage());
* stream, comparing the extension with the value of the Extension }
* attribute. The comparison shall be case-insensitive ASCII. }
* </p><p> }
* 4. If there is a Default element with a matching Extension attribute,
* return the value of its ContentType attribute. No further action is
* required.
* </p><p>
* 5. If neither Override nor Default elements with matching attributes are
* found for the specified part name, the implementation shall not map this
* part name to a part.
* </p>
* @param partName
* The URI part to check.
* @return The content type associated with the URI (in case of an override
* content type) or the extension (in case of default content type),
* else <code>null</code>.
*
* @exception OpenXML4JRuntimeException
* Throws if the content type manager is not able to find the
* content from an existing part.
*/
public String getContentType(PackagePartName partName) {
if (partName == null)
throw new IllegalArgumentException("partName");
if ((this.overrideContentType != null) /**
&& this.overrideContentType.containsKey(partName)) * Check if the specified content type is already register.
return this.overrideContentType.get(partName); *
* @param contentType
* The content type to check.
* @return <code>true</code> if the specified content type is already
* register, then <code>false</code>.
*/
public boolean isContentTypeRegister(String contentType) {
if (contentType == null)
throw new IllegalArgumentException("contentType");
String extension = partName.getExtension().toLowerCase(Locale.ROOT); return (this.defaultContentType.values().contains(contentType) || (this.overrideContentType != null && this.overrideContentType
if (this.defaultContentType.containsKey(extension)) .values().contains(contentType)));
return this.defaultContentType.get(extension); }
/* /**
* [M2.4] : The package implementer shall require that the Content Types * Get the content type for the specified part, if any.
* stream contain one of the following for every part in the package: * <p>
* One matching Default element, One matching Override element, Both a * Rule [M2.9]: To get the content type of a part, the package implementer
* matching Default element and a matching Override element, in which * shall perform the steps described in &#167;9.1.2.4:
* case the Override element takes precedence. * </p><p>
*/ * 1. Compare the part name with the values specified for the PartName
if (this.container != null && this.container.getPart(partName) != null) { * attribute of the Override elements. The comparison shall be
throw new OpenXML4JRuntimeException( * case-insensitive ASCII.
"Rule M2.4 exception : this error should NEVER happen! If you can provide the triggering file, then please raise a bug at https://bz.apache.org/bugzilla/enter_bug.cgi?product=POI and attach the file that triggers it, thanks!"); * </p><p>
} * 2. If there is an Override element with a matching PartName attribute,
return null; * return the value of its ContentType attribute. No further action is
} * required.
* </p><p>
* 3. If there is no Override element with a matching PartName attribute,
* then a. Get the extension from the part name by taking the substring to
* the right of the rightmost occurrence of the dot character (.) from the
* rightmost segment. b. Check the Default elements of the Content Types
* stream, comparing the extension with the value of the Extension
* attribute. The comparison shall be case-insensitive ASCII.
* </p><p>
* 4. If there is a Default element with a matching Extension attribute,
* return the value of its ContentType attribute. No further action is
* required.
* </p><p>
* 5. If neither Override nor Default elements with matching attributes are
* found for the specified part name, the implementation shall not map this
* part name to a part.
* </p>
* @param partName
* The URI part to check.
* @return The content type associated with the URI (in case of an override
* content type) or the extension (in case of default content type),
* else <code>null</code>.
*
* @exception OpenXML4JRuntimeException
* Throws if the content type manager is not able to find the
* content from an existing part.
*/
public String getContentType(PackagePartName partName) {
if (partName == null)
throw new IllegalArgumentException("partName");
/** if ((this.overrideContentType != null)
* Clear all content types. && this.overrideContentType.containsKey(partName))
*/ return this.overrideContentType.get(partName);
public void clearAll() {
this.defaultContentType.clear();
if (this.overrideContentType != null)
this.overrideContentType.clear();
}
/** String extension = partName.getExtension().toLowerCase(Locale.ROOT);
* Clear all override content types. if (this.defaultContentType.containsKey(extension))
* return this.defaultContentType.get(extension);
*/
public void clearOverrideContentTypes() {
if (this.overrideContentType != null)
this.overrideContentType.clear();
}
/** /*
* Parse the content types part. * [M2.4] : The package implementer shall require that the Content Types
* * stream contain one of the following for every part in the package:
* @throws InvalidFormatException * One matching Default element, One matching Override element, Both a
* Throws if the content type doesn't exist or the XML format is * matching Default element and a matching Override element, in which
* invalid. * case the Override element takes precedence.
*/ */
private void parseContentTypesFile(InputStream in) if (this.container != null && this.container.getPart(partName) != null) {
throws InvalidFormatException { throw new OpenXML4JRuntimeException(
try { "Rule M2.4 exception : Part \'" + partName +
Document xmlContentTypetDoc = DocumentHelper.readDocument(in); "\' not found - this error should NEVER happen! If you can provide the triggering file, then please raise a bug at https://bz.apache.org/bugzilla/enter_bug.cgi?product=POI and attach the file that triggers it, thanks!");
}
return null;
}
// Default content types /**
NodeList defaultTypes = xmlContentTypetDoc.getDocumentElement().getElementsByTagNameNS(TYPES_NAMESPACE_URI, DEFAULT_TAG_NAME); * Clear all content types.
int defaultTypeCount = defaultTypes.getLength(); */
for (int i = 0; i < defaultTypeCount; i++) { public void clearAll() {
this.defaultContentType.clear();
if (this.overrideContentType != null)
this.overrideContentType.clear();
}
/**
* Clear all override content types.
*
*/
public void clearOverrideContentTypes() {
if (this.overrideContentType != null)
this.overrideContentType.clear();
}
/**
* Parse the content types part.
*
* @throws InvalidFormatException
* Throws if the content type doesn't exist or the XML format is
* invalid.
*/
private void parseContentTypesFile(InputStream in)
throws InvalidFormatException {
try {
Document xmlContentTypetDoc = DocumentHelper.readDocument(in);
// Default content types
NodeList defaultTypes = xmlContentTypetDoc.getDocumentElement().getElementsByTagNameNS(TYPES_NAMESPACE_URI, DEFAULT_TAG_NAME);
int defaultTypeCount = defaultTypes.getLength();
for (int i = 0; i < defaultTypeCount; i++) {
Element element = (Element) defaultTypes.item(i); Element element = (Element) defaultTypes.item(i);
String extension = element.getAttribute(EXTENSION_ATTRIBUTE_NAME); String extension = element.getAttribute(EXTENSION_ATTRIBUTE_NAME);
String contentType = element.getAttribute(CONTENT_TYPE_ATTRIBUTE_NAME); String contentType = element.getAttribute(CONTENT_TYPE_ATTRIBUTE_NAME);
addDefaultContentType(extension, contentType); addDefaultContentType(extension, contentType);
} }
// Overriden content types // Overriden content types
NodeList overrideTypes = xmlContentTypetDoc.getDocumentElement().getElementsByTagNameNS(TYPES_NAMESPACE_URI, OVERRIDE_TAG_NAME); NodeList overrideTypes = xmlContentTypetDoc.getDocumentElement().getElementsByTagNameNS(TYPES_NAMESPACE_URI, OVERRIDE_TAG_NAME);
int overrideTypeCount = overrideTypes.getLength(); int overrideTypeCount = overrideTypes.getLength();
for (int i = 0; i < overrideTypeCount; i++) { for (int i = 0; i < overrideTypeCount; i++) {
Element element = (Element) overrideTypes.item(i); Element element = (Element) overrideTypes.item(i);
URI uri = new URI(element.getAttribute(PART_NAME_ATTRIBUTE_NAME)); URI uri = new URI(element.getAttribute(PART_NAME_ATTRIBUTE_NAME));
PackagePartName partName = PackagingURIHelper.createPartName(uri); PackagePartName partName = PackagingURIHelper.createPartName(uri);
String contentType = element.getAttribute(CONTENT_TYPE_ATTRIBUTE_NAME); String contentType = element.getAttribute(CONTENT_TYPE_ATTRIBUTE_NAME);
addOverrideContentType(partName, contentType); addOverrideContentType(partName, contentType);
} }
} catch (URISyntaxException | IOException | SAXException e) { } catch (URISyntaxException | IOException | SAXException e) {
throw new InvalidFormatException(e.getMessage()); throw new InvalidFormatException(e.getMessage());
} }
} }
/** /**
* Save the contents type part. * Save the contents type part.
* *
* @param outStream * @param outStream
* The output stream use to save the XML content of the content * The output stream use to save the XML content of the content
* types part. * types part.
* @return <b>true</b> if the operation success, else <b>false</b>. * @return <b>true</b> if the operation success, else <b>false</b>.
*/ */
public boolean save(OutputStream outStream) { public boolean save(OutputStream outStream) {
Document xmlOutDoc = DocumentHelper.createDocument(); Document xmlOutDoc = DocumentHelper.createDocument();
// Building namespace // Building namespace
Element typesElem = xmlOutDoc.createElementNS(TYPES_NAMESPACE_URI, TYPES_TAG_NAME); Element typesElem = xmlOutDoc.createElementNS(TYPES_NAMESPACE_URI, TYPES_TAG_NAME);
xmlOutDoc.appendChild(typesElem); xmlOutDoc.appendChild(typesElem);
// Adding default types // Adding default types
for (Entry<String, String> entry : defaultContentType.entrySet()) { for (Entry<String, String> entry : defaultContentType.entrySet()) {
appendDefaultType(typesElem, entry); appendDefaultType(typesElem, entry);
} }
// Adding specific types if any exist // Adding specific types if any exist
if (overrideContentType != null) { if (overrideContentType != null) {
for (Entry<PackagePartName, String> entry : overrideContentType for (Entry<PackagePartName, String> entry : overrideContentType
.entrySet()) { .entrySet()) {
appendSpecificTypes(typesElem, entry); appendSpecificTypes(typesElem, entry);
} }
} }
xmlOutDoc.normalize(); xmlOutDoc.normalize();
// Save content in the specified output stream // Save content in the specified output stream
return this.saveImpl(xmlOutDoc, outStream); return this.saveImpl(xmlOutDoc, outStream);
} }
/** /**
* Use to append specific type XML elements, use by the save() method. * Use to append specific type XML elements, use by the save() method.
* *
* @param root * @param root
* XML parent element use to append this override type element. * XML parent element use to append this override type element.
* @param entry * @param entry
* The values to append. * The values to append.
* @see #save(java.io.OutputStream) * @see #save(java.io.OutputStream)
*/ */
private void appendSpecificTypes(Element root, private void appendSpecificTypes(Element root,
Entry<PackagePartName, String> entry) { Entry<PackagePartName, String> entry) {
Element specificType = root.getOwnerDocument().createElementNS(TYPES_NAMESPACE_URI, OVERRIDE_TAG_NAME); Element specificType = root.getOwnerDocument().createElementNS(TYPES_NAMESPACE_URI, OVERRIDE_TAG_NAME);
specificType.setAttribute(PART_NAME_ATTRIBUTE_NAME, entry.getKey().getName()); specificType.setAttribute(PART_NAME_ATTRIBUTE_NAME, entry.getKey().getName());
specificType.setAttribute(CONTENT_TYPE_ATTRIBUTE_NAME, entry.getValue()); specificType.setAttribute(CONTENT_TYPE_ATTRIBUTE_NAME, entry.getValue());
root.appendChild(specificType); root.appendChild(specificType);
} }
/** /**
* Use to append default types XML elements, use by the save() method. * Use to append default types XML elements, use by the save() method.
* *
* @param root * @param root
* XML parent element use to append this default type element. * XML parent element use to append this default type element.
* @param entry * @param entry
* The values to append. * The values to append.
* @see #save(java.io.OutputStream) * @see #save(java.io.OutputStream)
*/ */
private void appendDefaultType(Element root, Entry<String, String> entry) { private void appendDefaultType(Element root, Entry<String, String> entry) {
Element defaultType = root.getOwnerDocument().createElementNS(TYPES_NAMESPACE_URI, DEFAULT_TAG_NAME); Element defaultType = root.getOwnerDocument().createElementNS(TYPES_NAMESPACE_URI, DEFAULT_TAG_NAME);
defaultType.setAttribute(EXTENSION_ATTRIBUTE_NAME, entry.getKey()); defaultType.setAttribute(EXTENSION_ATTRIBUTE_NAME, entry.getKey());
defaultType.setAttribute(CONTENT_TYPE_ATTRIBUTE_NAME, entry.getValue()); defaultType.setAttribute(CONTENT_TYPE_ATTRIBUTE_NAME, entry.getValue());
root.appendChild(defaultType); root.appendChild(defaultType);
} }
/** /**
* Specific implementation of the save method. Call by the save() method, * Specific implementation of the save method. Call by the save() method,
* call before exiting. * call before exiting.
* *
* @param out * @param out
* The output stream use to write the content type XML. * The output stream use to write the content type XML.
*/ */
public abstract boolean saveImpl(Document content, OutputStream out); public abstract boolean saveImpl(Document content, OutputStream out);
} }