package com.limegroup.gnutella.gui.xml;
import com.limegroup.gnutella.gui.GUIMediator;
import com.limegroup.gnutella.xml.LimeXMLDocument;
import com.limegroup.gnutella.xml.LimeXMLSchema;
import com.limegroup.gnutella.xml.LimeXMLSchemaRepository;
import com.limegroup.gnutella.xml.SchemaFieldInfo;
import com.limegroup.gnutella.xml.XMLStringUtils;
import com.limegroup.gnutella.util.CommonUtils;
import com.limegroup.gnutella.util.NameValue;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
/**
* Various GUI-related XML utilities.
*/
public class XMLUtils {
private static final Log LOG = LogFactory.getLog(XMLUtils.class);
/**
* A mapping of Schema -> ResourceBundle for that schema.
* These ResourceBundles are only used (and only created)
* if someone attempts to retrieve a resource that does not exist
* in the main ResourceBundle.
*/
private static Map bundles;
private XMLUtils() {}
/**
* Returns a list of name/values for this document, suitable for display.
*/
public static List getDisplayList(LimeXMLDocument doc) {
List data = new LinkedList();
LimeXMLSchema schema = doc.getSchema();
List fields = schema.getCanonicalizedFields();
// For each name/value pair...
for( Iterator j = fields.iterator(); j.hasNext(); ) {
SchemaFieldInfo sfi = (SchemaFieldInfo)j.next();
String name = sfi.getCanonicalizedFieldName();
String value = doc.getValue(name);
if(value == null || sfi.isHidden())
continue;
NameValue pair = getDisplayPair(sfi, value, schema);
data.add(pair.getName() + ": " + pair.getValue());
}
return data;
}
/**
* Returns the NameValue pair of the display name for the field
* and a valid visual representation of the value.
*/
public static NameValue getDisplayPair(SchemaFieldInfo field, String value, LimeXMLSchema schema) {
String name = getResource(field.getCanonicalizedFieldName());
if(field.getJavaType() == Date.class) {
try {
value = CommonUtils.seconds2time(Integer.parseInt(value));
} catch (NumberFormatException ignored) {}
}
return new NameValue(name, value);
}
/**
* Gets the resource for the given string.
*/
public static String getResource(String field) {
try {
return GUIMediator.getStringResource("XML_" + field);
} catch(MissingResourceException mre) {
if(LOG.isWarnEnabled())
LOG.warn("Missing main resource for: " + field + ", falling back.", mre);
return fallbackResource(field, getBundleForField(field));
}
}
/**
* Gets the title of the schema based on the field.
*/
public static String getTitleForSchemaFromField(String field) {
// not an XML field? ignore.
if(!field.endsWith("__"))
return null;
// The canonicalKey is always going to be x__x__<other stuff here>
int idx1 = field.indexOf(XMLStringUtils.DELIMITER) + 2;
int idx2 = field.indexOf(XMLStringUtils.DELIMITER, idx1);
return getResource(field.substring(0, idx2));
}
/**
* Gets the correct display name for the given schemaURI.
*/
public static String getTitleForSchemaURI(String schemaURI) {
LimeXMLSchema schema = LimeXMLSchemaRepository.instance().getSchema(schemaURI);
if(schema != null)
return getTitleForSchema(schema);
else
return null;
}
/**
* Gets the correct display name for the given schema.
*/
public static String getTitleForSchema(LimeXMLSchema schema) {
return getResource(schema.getRootXMLName() + XMLStringUtils.DELIMITER + schema.getInnerXMLName());
}
/**
* Gets the resource bundle for the given field name.
*/
private static ResourceBundle getBundleForField(String field) {
if(bundles == null)
loadBundles();
return (ResourceBundle)bundles.get(getDescriptionFromField(field));
}
/**
* Gets the description of the schema from a field name.
*
* That is, where the field is called audios__audio__field,
* the description this returns is "audio".
*/
private static String getDescriptionFromField(String field) {
// The canonicalKey is always going to be x__x__<other stuff here>
int idx1 = field.indexOf(XMLStringUtils.DELIMITER) + 2;
int idx2 = field.indexOf(XMLStringUtils.DELIMITER, idx1);
if(idx2 == -1)
idx2 = field.length();
return field.substring(idx1, idx2);
}
/**
* Populates the bundles map with the resource bundles we know about.
*/
private static void loadBundles() {
bundles = new HashMap();
Collection schemas = LimeXMLSchemaRepository.instance().getAvailableSchemas();
for(Iterator i = schemas.iterator(); i.hasNext(); ) {
LimeXMLSchema schema = (LimeXMLSchema)i.next();
String key = schema.getDescription();
try {
bundles.put(key, GUIMediator.getXMLResourceBundle(key));
} catch(MissingResourceException mre) {
if(LOG.isWarnEnabled())
LOG.warn("Missing resource bundle for schema: " + key, mre);
}
}
}
/**
* Gets the resource from the XML resource bundles for that field.
*/
private static String fallbackResource(String field, ResourceBundle bundle) {
if(bundle != null) {
try {
return bundle.getString(field);
} catch(MissingResourceException mre) {
if(LOG.isWarnEnabled())
LOG.warn("Missing fallback resource for: " + field + ", capitalizing field name.", mre);
}
}
return processField(field);
}
/**
* Capitalizes the first letter of the string and converts '_' to ' '.
*
* That is, where the name is "field_name", this will return "Field name".
*/
private static String formatFieldName(String name) {
return name.substring(0, 1).toUpperCase(Locale.US) + name.substring(1).replace('_', ' ').trim();
}
/**
* Determines the field's name based on the field.
*
* That is, where the field is audios__audio__field,
* this will return "Field".
*/
private static String processField(String field) {
int endIdx, startIdx;
if(field.endsWith(XMLStringUtils.DELIMITER))
endIdx = field.length() - 2; // 2 == XMLStringUtils.DELIMITER.length()
else
endIdx = field.length();
startIdx = field.lastIndexOf(XMLStringUtils.DELIMITER, endIdx-1) + 2;
return formatFieldName(field.substring(startIdx, endIdx));
}
}