/*
* � Copyright IBM Corp. 2011, 2014
*
* 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.
*/
/*
* Author: Maire Kehoe (mkehoe@ie.ibm.com)
* Date: 18 Sep 2006
* PropertiesExtractor.java
*/
package com.ibm.xsp.tools.flatten;
import java.util.*;
import org.w3c.dom.*;
import com.ibm.xsp.registry.parse.ElementUtil;
import com.ibm.xsp.registry.parse.FileConstants;
/**
* @author Maire Kehoe (mkehoe@ie.ibm.com)
* 18 Sep 2006
*/
public class PropertiesExtractor {
private static final boolean COMMENT_PROPERTIES_FILES = true;
private static final boolean LOCALIZE_RENDERERS_AND_CONTENTS = false;
public static String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
private static final String DESCR_SUFFIX = "descr";
private static final String NAME_SUFFIX = "name";
private static final String[] TWO_STRINGS = new String[2];
private static final Element[] TWO_ELEMENTS = new Element[2];
public static final String[] MATCH_LOCALIZABLE = new String[]{
"display-name", "description"};
/**
* Does 2 things: <br>
* 1) makes a multi-line String of .properties file contents containing the
* labels in the Document. <br>
* 2) optionally, if useKeysInDocument, changes the document to replace the
* labels with references to the .properties file keys
*
* @param document
* @return
*/
public String changeDocument(Document document, boolean useKeysInDocument, boolean printWarnings){
List<LocalizableLocation> locations = new ArrayList<LocalizableLocation>();
addLocationsToList(printWarnings, document, locations );
qualifyDuplicatePrefixes(locations);
if( useKeysInDocument ){
writePropertiesKeysToDocument(locations, document);
}
List<String> contentsList = toPropertiesFileLines(printWarnings, locations);
String fileContents = toPropertiesFileString(contentsList);
return fileContents;
}
private void writePropertiesKeysToDocument(List<LocalizableLocation> locations, Document document) {
for (LocalizableLocation loc : locations) {
String prefix = loc.suggestedPrefix;
if( null != loc.displayName ){
String key = "%"+prefix +NAME_SUFFIX+"%";
Element elem = loc.getDisplayNameElem();
writeKeyToElement(document, key, elem);
}
if( null != loc.description ){
String key = "%"+prefix +DESCR_SUFFIX+"%";
Element elem = loc.getDescriptionElem();
writeKeyToElement(document, key, elem);
}
}
}
private void writeKeyToElement(Document document, String key, Element elem) {
// remove existing children
NodeList kids = elem.getChildNodes();
while( kids.getLength() > 0 ){
Node child = kids.item(0);
elem.removeChild(child);
}
// add new child
Text newChild = document.createTextNode(key);
elem.appendChild(newChild);
}
private List<String> toPropertiesFileLines(boolean printWarnings, List<LocalizableLocation> locations) {
// build all lines into a list, ignoring duplicates
List<String> fileContents = new ArrayList<String>(locations.size() * 2);
for (LocalizableLocation loc : locations) {
String key = loc.suggestedPrefix;
if( null != loc.displayName ){
String message = loc.displayName;
addUniqueLine(printWarnings, fileContents, key, NAME_SUFFIX,
message, loc.displayNameComment);
}
if( null != loc.description ){
String message = loc.description;
checkDescrLength(printWarnings, loc, message);
addUniqueLine(printWarnings, fileContents, key, DESCR_SUFFIX,
message, loc.descriptionComment);
}
}
return fileContents;
}
private String toPropertiesFileString(List<String> fileContents) {
// now that duplicates have been removed, convert the list to a string
StringBuffer contents = new StringBuffer();
for (String line : fileContents) {
contents.append(line).append(NEWLINE);
}
return contents.toString();
}
private void addUniqueLine(boolean printWarnings, List<String> fileContents,
String key, String suffix, String message, String comment) {
String nameKey = key + suffix;
message = getAsResourceString(printWarnings, nameKey, message);
String line = nameKey + "= " + message;
if( ! fileContents.contains(line) ){
if( null != comment ){
fileContents.add(comment);
}
fileContents.add(line);
}
}
/**
* For all duplicate attribute. and property. prefixes
* append the prefix for the corresponding definition.
*/
private void qualifyDuplicatePrefixes(List<LocalizableLocation> locations) {
// sort by prefix
locations = new ArrayList<LocalizableLocation>(locations);
Collections.sort(locations);
// null used as a marker for the last in the list
locations.add(null);
String lastPrefix = null;
LocalizableLocation lastLoc = null;
List<LocalizableLocation> locsWithCurrentPrefix = new ArrayList<LocalizableLocation>();
Map<String, String> descrAndNameToPrefix = new HashMap<String, String>();
for (LocalizableLocation loc : locations) {
if(null != loc && loc.suggestedPrefix.equals(lastPrefix) ){
if( locsWithCurrentPrefix.size() == 0 ){
// found the first duplicate
locsWithCurrentPrefix.add(lastLoc);
}
// found another duplicate
locsWithCurrentPrefix.add(loc);
}else if( locsWithCurrentPrefix.size() != 0){
// gone past the last duplicate
qualifyDuplicatePrefixSet(locsWithCurrentPrefix, descrAndNameToPrefix);
} else if( null != lastLoc ){
// only 1 element with that short prefix
if( null != lastLoc.readPrefix ){
lastLoc.suggestedPrefix = lastLoc.readPrefix;
}
}
if( null != loc ){
lastPrefix = loc.suggestedPrefix;
lastLoc = loc;
}
}
if( locsWithCurrentPrefix.size() != 0 ){
qualifyDuplicatePrefixSet(locsWithCurrentPrefix, descrAndNameToPrefix);
}
}
private void qualifyDuplicatePrefixSet(List<LocalizableLocation> locsWithCurrentPrefix, Map<String, String> descrAndNameToPrefix) {
// if any key read from config file for each (display-name, description)
// that prefix gets used instead of the full prefix.
for (LocalizableLocation dupe : locsWithCurrentPrefix) {
if( null != dupe.readPrefix ){
String key = dupe.displayName + "#"+dupe.description;
descrAndNameToPrefix.put(key, dupe.readPrefix);
}
}
for (LocalizableLocation dupe : locsWithCurrentPrefix) {
// find an existing fullPrefix for this name/description pair
String key = dupe.displayName + "#"+dupe.description;
String readOrFullPrefix = descrAndNameToPrefix.get(key);
if( null == readOrFullPrefix ){
readOrFullPrefix = computeFullPrefix(dupe);
if( descrAndNameToPrefix.containsValue(readOrFullPrefix) ){
// not allow duplicate full prefixes for different messages
throw new RuntimeException("More than one property or attribute with full prefix "+readOrFullPrefix);
}
descrAndNameToPrefix.put(key, readOrFullPrefix);
}
dupe.suggestedPrefix = readOrFullPrefix;
}
descrAndNameToPrefix.clear();
locsWithCurrentPrefix.clear();
}
private String computeFullPrefix(LocalizableLocation loc) {
String elementType = loc.defRoot.getNodeName();
if (!(FileConstants.ATTRIBUTE.equals(elementType) || FileConstants.PROPERTY
.equals(elementType))) {
throw new RuntimeException("Duplicate prefix ("
+ loc.suggestedPrefix + ") for an " + elementType
+ " element.");
}
Element defElem = null;
Node ancestor = loc.defRoot.getParentNode();
while( Node.ELEMENT_NODE != ancestor.getNodeType() ){
ancestor = ancestor.getParentNode();
}
if( ancestor instanceof Element ){
defElem = (Element) ancestor;
}else{
throw new RuntimeException();
}
String containerPrefix = computeSuggestedPrefix( defElem );
String fullPrefix = loc.suggestedPrefix+containerPrefix;
return fullPrefix;
}
private String getAsResourceString(boolean printWarnings, String key, String localizableVal) {
boolean startsWithWhitespace = localizableVal.length() < 1 ? false
: Character.isWhitespace(localizableVal.charAt(0));
if( startsWithWhitespace && '\n' != localizableVal.charAt(0) ){
// put in a backslash to escape the whitespace
// else it won't appear at the start of the loaded string
localizableVal = "\\"+ localizableVal; //$NON-NLS-1$
}
checkMessageNewlines(printWarnings, key, localizableVal);
localizableVal = localizableVal.replaceAll("\n", "\\\\n\\\\"+NEWLINE);
return localizableVal;
}
private void addLocationsToList(boolean printWarnings, Document doc, List<LocalizableLocation> locs) {
Element root = doc.getDocumentElement();
// faces-config
LocalizableLocation loc = getLocalizableInExtension(root,
FileConstants.FACES_CONFIG_EXTENSION);
if( null != loc ){
loc.suggestedPrefix = root.getNodeName()+".";
if( !loc.isDescriptionAlreadyBundleRef || !loc.isDisplayNameAlreadyBundleRef ){
addToLocs(locs, loc);
}
}
for (Element i : ElementUtil.getChildren(root)) {
String elemName = i.getNodeName();
if( FileConstants.COMPONENT.equals(elemName) ){
addLocInRoot(printWarnings, locs, i);
addPropertyAndAttributeLocationsToList(printWarnings, i, locs, false);
// TODO handle facets also
continue;
}
if( FileConstants.COMPLEX_TYPE.equals(elemName) ){
addLocInRoot(printWarnings, locs, i);
addPropertyAndAttributeLocationsToList(printWarnings, i, locs, false);
continue;
}
if( FileConstants.CONVERTER.equals(elemName) ){
addLocInRoot(printWarnings, locs, i);
addPropertyAndAttributeLocationsToList(printWarnings, i, locs, false);
continue;
}
if( FileConstants.VALIDATOR.equals(elemName) ){
addLocInRoot(printWarnings, locs, i);
addPropertyAndAttributeLocationsToList(printWarnings, i, locs, false);
continue;
}
if( FileConstants.RENDER_KIT.equals(elemName) ){
String kitSuggestedPrefix = computeSuggestedPrefix(i);
String renderKitId = getRenderKitId(kitSuggestedPrefix);
// get localizable
loc = getLocalizableInRoot(i);
if( null != loc ){
loc.suggestedPrefix = kitSuggestedPrefix;
addToLocs(locs, loc);
}
checkPresent(printWarnings, i, loc);
// for all renderers
for (Element renderer : ElementUtil.getChildren(i, FileConstants.RENDERER)) {
if( LOCALIZE_RENDERERS_AND_CONTENTS ){
// get localizable
loc = getLocalizableInRoot(renderer);
if( null != loc ){
loc.suggestedPrefix = computeSuggestedPrefix(renderer, renderKitId);
addToLocs(locs, loc);
}
checkPresent(printWarnings, renderer, loc);
}
addPropertyAndAttributeLocationsToList(printWarnings, renderer, locs, true);
}
continue;
}
if( FileConstants.GROUP.equals(elemName) ){
// <group-type>com.ibm.xsp.foo</group-type>
addPropertyAndAttributeLocationsToList(printWarnings, i, locs, false);
// TODO handle facets also
continue;
}
if( FileConstants.FACES_CONFIG_EXTENSION.equals(elemName) ){
// do nothing
continue;
}
throw new RuntimeException(elemName);
// TODO (mkehoe) handle property-type and composite-component
// if( FileConstants.COMPOSITE_COMPONENT.equals(elemName) ){
// continue;
// }
// if( FileConstants.PROPERTY_TYPE.equals(elemName) ){
// // TODO (mkehoe) handle property-types
// continue;
// }
}
}
private void addLocInRoot(boolean printWarnings, List<LocalizableLocation> locs, Element defRoot) {
LocalizableLocation loc = getLocalizableInRoot(defRoot);
if( null != loc ){
loc.suggestedPrefix = computeSuggestedPrefix(defRoot);
if( !loc.isDescriptionAlreadyBundleRef || !loc.isDisplayNameAlreadyBundleRef ){
addToLocs(locs, loc);
}
}
checkPresent(printWarnings, defRoot, loc);
}
private void checkPresent(boolean printWarnings, Element defRoot, LocalizableLocation loc) {
if( ! printWarnings ){
return;
}
String suggestedPrefix = (null == loc)? computeSuggestedPrefix(defRoot) : loc.suggestedPrefix;
if( null == loc || null == loc.description && null == loc.displayName ){
if( ! suggestedPrefix.startsWith("render-kit.html.")){
System.err.println("PropertiesExtractor.checkPresent() "
+ "description and display-name missing for " + suggestedPrefix);
}
// else don't worry about missing HTML_BASIC descr/name
return;
}
if( null == loc.description ){
if( ! LOCALIZE_RENDERERS_AND_CONTENTS && suggestedPrefix.startsWith("render-kit.")){
// not localize description for render-kits
}else{
System.err.println("PropertiesExtractor.checkPresent() "
+ "description missing for " + suggestedPrefix
+ " display-name=" + loc.displayName);
}
}
if( null == loc.displayName ){
System.err.println("PropertiesExtractor.checkPresent() "
+ "display-name missing for " + suggestedPrefix
+ " description=" + loc.description);
}
if( loc.isDescriptionAlreadyBundleRef && loc.isDisplayNameAlreadyBundleRef ){
// System.out.println("PropertiesExtractor.checkPresent() " +
// "Using existing bundle refs: " +loc.displayName+
// " "+loc.description);
}else if( loc.isDescriptionAlreadyBundleRef || loc.isDisplayNameAlreadyBundleRef ){
System.err.println("PropertiesExtractor.checkPresent()"
+ "Expect both descr and name to be bundle refs, this util cannot handle name:"
+ loc.displayName + " descr: " + loc.description);
}
}
private void checkDescrLength(boolean printWarnings, LocalizableLocation loc, String message) {
if( ! printWarnings ){
return;
}
int nameLen = (null == loc.displayName? 0 : loc.displayName.length()) ;
if( message.length() < nameLen + 5 ){
// TODO skipping "Triggered" descriptions as they're often short
if( message.startsWith("Triggered") ){
return;
}
String descrKey = loc.suggestedPrefix+DESCR_SUFFIX;
if( "complex-type.setComponentMode.descr".equals( descrKey) ){
// description is just "Changes the component mode."
return;
}
System.err.println("PropertiesExtractor.checkDescrLength() "
+ "description probably too short " + descrKey + "= "
+ message.replaceAll("\n", "\\\\n"));
}
}
private void checkMessageNewlines(boolean printWarnings, String key, String message) {
if( ! printWarnings ){
return;
}
if( -1 != message.indexOf('\n')){
String toDisplay = "\n>\t"+message.replaceAll("\n", "\n>\t");
System.err.println("PropertiesExtractor.checkMessageNewlines() "
+ "message contains newlines for " + key + " :"
+ toDisplay);
}
}
private String computeSuggestedPrefix(Element e, String renderKitId) {
String prefix = e.getNodeName()+"."+renderKitId+".";
String compFamily = ElementUtil.extractValue(e,FileConstants.COMPONENT_FAMILY);
compFamily = compFamily.substring(compFamily.lastIndexOf('.')+1);
String rendererType = ElementUtil.extractValue(e,FileConstants.RENDERER_TYPE);
rendererType = rendererType.substring(rendererType.lastIndexOf('.')+1);
prefix += compFamily +"_"+ rendererType+".";
return prefix;
}
private String getRenderKitId(String kitSuggestedPrefix) {
return kitSuggestedPrefix.substring(kitSuggestedPrefix.indexOf('.')+1, kitSuggestedPrefix.length() - 1);
}
private String computeSuggestedPrefix(Element e) {
String elemName = e.getNodeName();
String prefix = elemName+".";
if( FileConstants.COMPONENT.equals(elemName) ){
String tagName = ElementUtil.extractValue(
ElementUtil.getExtension(e,
FileConstants.COMPONENT_EXTENSION),
FileConstants.TAG_NAME);
if ( null == tagName ) {
tagName = ElementUtil.extractValue(e,
FileConstants.COMPONENT_TYPE);
}
prefix += tagName;
}
else if( FileConstants.VALIDATOR.equals(elemName)){
String id = ElementUtil.extractValue(e,FileConstants.VALIDATOR_ID);
prefix += id;
}
else if( FileConstants.COMPLEX_TYPE.equals(elemName) ){
String tagName = ElementUtil.extractValue(
ElementUtil.getExtension(e,
FileConstants.COMPLEX_EXTENSION),
FileConstants.TAG_NAME);
if ( null == tagName ) {
tagName = ElementUtil.extractValue(e,
FileConstants.COMPLEX_ID);
}
// String tagName = ElementUtil.extractValue(e,FileConstants.COMPLEX_NAME);
prefix += tagName;
}
else if( FileConstants.CONVERTER.equals(elemName) ){
String id = ElementUtil.extractValue(e,FileConstants.CONVERTER_ID);
// extra .id is a left-over from when used to have converter-for-class converters
prefix += "id."+id;
}
else if( FileConstants.RENDER_KIT.equals(elemName) ){
// calculate render-kit-id or hardcoded alias
String renderKitId = ElementUtil.extractValue(e,FileConstants.RENDER_KIT_ID);
if ( null == renderKitId
|| "HTML_BASIC".equals(renderKitId)) {
renderKitId = "html";
}
prefix += renderKitId;
}
else if( FileConstants.RENDERER.equals(elemName) ){
String kitSuggestedPrefix = computeSuggestedPrefix((Element)e.getParentNode());
String renderKitId = getRenderKitId(kitSuggestedPrefix);
return computeSuggestedPrefix(e, renderKitId);
}
else if( FileConstants.PROPERTY.equals(elemName) ){
String propName = ElementUtil.extractValue(e, FileConstants.PROPERTY_NAME);
prefix += propName;
}
else if( FileConstants.ATTRIBUTE.equals(elemName) ){
String propName = ElementUtil.extractValue(e, FileConstants.ATTRIBUTE_NAME);
prefix += propName;
}
else if( FileConstants.GROUP.equals(elemName) ){
String keySuffix = getKeySuffix(e);
if( null == keySuffix ){
String msg = "No .properties key prefix for the <group> ";
msg += ElementUtil.extractValue(e, FileConstants.GROUP_TYPE);
msg += ". Please provide a key-suffix for the group like so: "
+"<!-- key-suffix: events --> "
+"Or provide an explicit key comment for each property, "
+"like so: <!-- key: property.something. -->";
throw new RuntimeException(msg );
}
prefix += keySuffix;
}
else{
throw new RuntimeException(elemName);
}
prefix += ".";
if( -1 != prefix.indexOf("null") ){
throw new RuntimeException("Bad key prefix "+prefix);
}
return prefix;
}
private void addToLocs(List<LocalizableLocation> locs, LocalizableLocation loc) {
if( ! locs.contains(loc) ){
locs.add(loc);
}
}
private void addPropertyAndAttributeLocationsToList(boolean printWarnings, Element defRoot, List<LocalizableLocation> locs, boolean isRendererContents) {
LocalizableLocation loc;
for (Element elem : ElementUtil.getChildren(defRoot)) {
String elemName = elem.getNodeName();
boolean isProp = FileConstants.PROPERTY.equals(elemName);
if( !isProp && !FileConstants.ATTRIBUTE.equals(elemName)){
continue;
}
if( isRendererContents && !LOCALIZE_RENDERERS_AND_CONTENTS ){
// don't warn about missing renderer attribute descr/names
continue;
}
// get localizable
loc = getLocalizableInRoot(elem);
if( null != loc ){
String propName = ElementUtil.extractValue(elem,
(isProp ? FileConstants.PROPERTY_NAME
: FileConstants.ATTRIBUTE_NAME));
loc.suggestedPrefix = elemName + "."+ propName+".";
if( !loc.isDescriptionAlreadyBundleRef || !loc.isDisplayNameAlreadyBundleRef ){
addToLocs(locs, loc);
}
}
checkPresent(printWarnings, elem, loc);
}
}
private LocalizableLocation getLocalizableInRoot(Element root) {
Iterable<Element> children = ElementUtil.getChildren(root);
extractTagLessValues(children, MATCH_LOCALIZABLE, TWO_STRINGS, TWO_ELEMENTS);
LocalizableLocation loc = createNullOrLocation(root, TWO_STRINGS, TWO_ELEMENTS);
return loc;
}
private LocalizableLocation getLocalizableInExtension(Element root, String extensionName) {
Iterable<Element> configExt = ElementUtil.getExtensions(root, extensionName);
extractTagLessValues(configExt, MATCH_LOCALIZABLE, TWO_STRINGS, TWO_ELEMENTS);
LocalizableLocation loc = createNullOrLocation(root, TWO_STRINGS, TWO_ELEMENTS);
return loc;
}
private LocalizableLocation createNullOrLocation(Element root, String[] values, Element[] elements) {
values[0] = getTrimmed(values[0]);
values[1] = getTrimmed(values[1]);
if( values[0] != null || values[1] != null){
LocalizableLocation loc = new LocalizableLocation(root, values[0],
elements[0], values[1], elements[1]);
return loc;
}
return null;
}
private String getTrimmed(String value) {
if( value != null ){
value = value.trim();
if( 0 == value.length() ){
value = null;
}
}
return value;
}
private static void extractTagLessValues(Iterable<Element> parent,
String[] tagNames, String[] contents, Element [] sources){
// mostly copied from ElementUtil.extractValues
final int numArgs = tagNames.length;
Arrays.fill(contents, 0, numArgs, null);
Arrays.fill(sources, 0, numArgs, null);
for (Element i : parent) {
String tagName = i.getTagName();
for (int j = 0; j < tagNames.length; j++) {
if (tagName.equals(tagNames[j])) {
contents[j] = getElementContentsAsTagLessString(i);
if( contents[j].length() == 0){
contents[j] = null;
}else{
sources[j] = i;
}
}
}
}
return;
}
/**
* Note this method is also in BaseDesignerExtensionFactory
*/
private static String getElementContentsAsTagLessString(Element element) {
StringBuffer buffer = new StringBuffer();
appendChildren(element, buffer);
return buffer.toString();
}
private static void appendChildren(Element element, StringBuffer buffer) {
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if( child instanceof CDATASection || child instanceof Text ){
buffer.append( ((CharacterData)child).getData() );
}
if( child instanceof Element ){
appendChildren((Element)child, buffer);
}
}
}
private static String getComment(Element elem){
if( COMMENT_PROPERTIES_FILES && null != elem ){
Node previous = elem.getPreviousSibling();
previous = toBeforeWhitespace(previous);
if( previous instanceof Comment ){
String value = ((Comment)previous).getData().trim();
if( value.startsWith("#") ){
return value;
}
}
}
return null;
}
private static Node toBeforeWhitespace(Node previous) {
while( previous instanceof Text ){
if( ((Text)previous).getData().trim().length() != 0 ){
break;
}
previous = previous.getPreviousSibling();
}
return previous;
}
private static String getKeyPrefix(Element descr){
//<property>
// <!-- key: property.style. -->
// <!-- # 'style' should not be translated -->
// <description>A style to set</description>
if( null != descr ){
boolean allowPropsComment = true;
// for all comments before the description (moving backwards)
for (Node previous = toBeforeWhitespace(descr.getPreviousSibling());
previous instanceof Comment;
previous = toBeforeWhitespace(previous.getPreviousSibling())) {
String value = ((Comment)previous).getData().trim();
if( value.startsWith("key:") ){
String prefix = value.substring("key:".length());
prefix = prefix.trim();
if( 0 == prefix.length() ){
// key was empty
return null;
}
return prefix;
}else if( allowPropsComment && value.startsWith("#") ){
// not break
allowPropsComment = false;
continue;
}
}
}
return null;
}
private static String getKeySuffix(Element group){
//<group>
// <!-- some comment -->
// <!-- key-suffix: events -->
// <group-type>com.ibm.xsp.group.events</description>
Node child = group.getFirstChild();
if( null != child ){
for(Node i = child; i != null; i = i.getNextSibling() ){
if( i instanceof Text && 0 == ((Text)i).getData().trim().length() ){
// skip whitespace
continue;
}
if( i instanceof Comment ){
String value = ((Comment)i).getData().trim();
if( value.startsWith("key-suffix:") ){
String prefix = value.substring("key-suffix:".length());
prefix = prefix.trim();
if( 0 == prefix.length() ){
// key was empty
return null;
}
return prefix;
}
// ignore comment, not break.
continue;
}
// not a comment, so break
break;
}
}
return null;
}
private static boolean isBundleRef(String displayName) {
return null != displayName && displayName.startsWith("%");
}
private static class LocalizableLocation implements Comparable<LocalizableLocation>{
private String suggestedPrefix;
public String readPrefix;
public final Element defRoot;
public final String description;
private Element descriptionElem;
public String descriptionComment;
public boolean isDescriptionAlreadyBundleRef;
public final String displayName;
public Element displayNameElem;
public String displayNameComment;
public boolean isDisplayNameAlreadyBundleRef;
public LocalizableLocation(final Element defRoot,
final String displayName, final Element displayNameElem,
final String description, final Element descriptionElem) {
super();
this.defRoot = defRoot;
this.displayName = displayName;
setDisplayNameElem(displayNameElem);
this.isDisplayNameAlreadyBundleRef = isBundleRef(displayName);
this.description = description;
setDescriptionElem(descriptionElem);
this.isDescriptionAlreadyBundleRef = isBundleRef(displayName);
readPrefix = getKeyPrefix(descriptionElem);
}
public Element getDescriptionElem() {
return descriptionElem;
}
public void setDescriptionElem(Element descriptionElem) {
this.descriptionElem = descriptionElem;
this.descriptionComment = getComment(descriptionElem);
}
public Element getDisplayNameElem() {
return displayNameElem;
}
public void setDisplayNameElem(Element displayNameElem) {
this.displayNameElem = displayNameElem;
this.displayNameComment = getComment(displayNameElem);
}
public int compareTo(LocalizableLocation other) {
if( null == suggestedPrefix || null == other.suggestedPrefix ){
// the suggestedPrefix should be set immediately after the
// LocalizableLocation is created, before this method is called.
throw new RuntimeException("One of these is null: ("
+ suggestedPrefix + ") or (" + other.suggestedPrefix
+ ")");
}
int compare = suggestedPrefix.compareTo(other.suggestedPrefix);
if( 0 == compare ){
if( (null == displayName) != (null == other.displayName)){
return null == displayName? -1 : 1;
}
if( null != displayName ){
compare = displayName.compareTo(other.displayName);
}
}
if( 0 == compare ){
if( (null == description) != (null == other.description)){
return null == description? -1 : 1;
}
if( null != description ){
compare = description.compareTo(other.description);
}
}
if( 0 == compare ){
compare = toString().compareTo(other.toString());
}
return compare;
}
public String toString(){
return suggestedPrefix;
}
}
}