[Swift-commit] r6314 - in branches/faster: resources src/org/globus/swift/catalog/site src/org/griphyn/vdl/karajan/lib
hategan at ci.uchicago.edu
hategan at ci.uchicago.edu
Tue Feb 26 21:22:36 CST 2013
Author: hategan
Date: 2013-02-26 21:22:36 -0600 (Tue, 26 Feb 2013)
New Revision: 6314
Added:
branches/faster/resources/swift-sites-2.0.xsd
branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java
Removed:
branches/faster/src/org/globus/swift/catalog/site/Parser.java
Modified:
branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java
Log:
better sites file parsing
Added: branches/faster/resources/swift-sites-2.0.xsd
===================================================================
--- branches/faster/resources/swift-sites-2.0.xsd (rev 0)
+++ branches/faster/resources/swift-sites-2.0.xsd 2013-02-27 03:22:36 UTC (rev 6314)
@@ -0,0 +1,243 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema
+ xmlns="http://www.ci.uchicago.edu/swift/SwiftSites"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ targetNamespace="http://www.ci.uchicago.edu/swift/SwiftSites"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified" version="1.5">
+ <xs:simpleType name="ProfileNamespace">
+ <xs:annotation>
+ <xs:documentation>defines the legal namespaces of execution profiles.</xs:documentation>
+ </xs:annotation>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="vds"/>
+ <xs:enumeration value="swift"/>
+ <xs:enumeration value="env"/>
+ <xs:enumeration value="hints"/>
+ <xs:enumeration value="globus"/>
+ <xs:enumeration value="karajan"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:attributeGroup name="GlobusVersionInfo">
+ <xs:annotation>
+ <xs:documentation>A set of attributes to define a Globus version.</xs:documentation>
+ </xs:annotation>
+ <xs:attribute name="major" type="xs:pos
itiveInteger" use="required"/>
+ <xs:attribute name="minor" type="xs:nonNegativeInteger" use="required"/>
+ <xs:attribute name="patch" type="xs:nonNegativeInteger" use="optional"/>
+ </xs:attributeGroup>
+ <xs:simpleType name="OSType">
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="INTEL32::LINUX"/>
+ <xs:enumeration value="INTEL64::LINUX"/>
+ <xs:enumeration value="INTEL32::WINDOWS"/>
+ <xs:enumeration value="INTEL64::WINDOWS"/>
+ <xs:enumeration value="INTEL32::GENERIC_UNIX"/>
+ <xs:enumeration value="INTEL64::GENERIC_UNIX"/>
+ </xs:restriction>
+ </xs:simpleType>
+ <xs:element name="sites">
+ <xs:annotation>
+ <xs:documentation>Root element aggregating all sites information there is. This is the
+ preferred choice for version 2.0 of the sites file</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence maxOccurs="unbounded">
+ <xs:element name="site" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+
<xs:documentation>Describes a single site.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence maxOccurs="unbounded">
+ <xs:element ref="profile" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="env" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="workdirectory" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="scratch" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="execution" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="filesystem" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="application" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="sysinfo" type="OSType" use="optional" default="INTEL32::LINUX"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="config">
+ <xs:annotation>
+ <xs:documentation>roo
t element aggregating all sites information there is.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence maxOccurs="unbounded">
+ <xs:element name="pool" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Describes a single site.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence maxOccurs="unbounded">
+ <xs:element ref="profile" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="env" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element ref="workdirectory" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="scratch" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="execution" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="filesystem" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="gridftp" minOccurs="0" maxOccurs="1"/>
+ <xs:element ref="jobmanager" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ <xs:attribute name="han
dle" type="xs:ID" use="required"/>
+ <xs:attribute name="sysinfo" type="xs:string" use="optional" default="INTEL32::LINUX"/>
+
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="version" type="xs:decimal" use="optional" default="1.0"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="profile">
+ <xs:annotation>
+ <xs:documentation>Administrative profile defaults associated with a pool.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="namespace" type="ProfileNamespace" use="required"/>
+ <xs:attribute name="key" type="xs:string" use="required"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="workdirectory" default="/tmp">
+ <xs:annotation>
+ <xs:documentation>Each pool may have one and only one work directory mount point.</xs:documentation>
+ </xs:annotation>
+ <
xs:complexType>
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="total-size" type="xs:decimal" use="optional"/>
+ <xs:attribute name="free-size" type="xs:decimal" use="optional"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="scratch">
+ <xs:annotation>
+ <xs:documentation>TODO</xs:documentation>
+ </xs:annotation>
+ </xs:element>
+
+ <xs:element name="execution">
+ <xs:annotation>
+ <xs:documentation>Describes the mechanism used to submit jobs to the pool</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="provider" type="xs:string" use="required"/>
+ <xs:attribute name="jobManager" type="xs:string" use="optional"/>
+ <xs:attribute name="jobmanager" type="xs:string" use="optional"/>
+ <xs:attribute name="url" type="xs:string" use="optional" default="localhost"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="filesystem
">
+ <xs:annotation>
+ <xs:documentation>Describes the mechanism used to interact with the filesystem on the given pool</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="provider" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string" use="optional" default="localhost"/>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="gridftp">
+ <xs:annotation>
+ <xs:documentation>Each pool may have multiple gridftp servers.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="bandwidth" minOccurs="0" maxOccurs="unbounded">
+ <xs:annotation>
+ <xs:documentation>Stores the bandwidth informaion related to each gridftp server.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="dest-subnet" type="xs:string" use="required"/>
+ <xs:attribute name="avg-bandwidth" type="xs:float" use="required"/>
+ <xs:attribute name="
max-bandwidth" type="xs:float" use="required"/>
+ <xs:attribute name="min-bandwidth" type="xs:float" use="required"/>
+ <xs:attribute name="avg-bandwidth-range1" type="xs:float" use="optional"/>
+ <xs:attribute name="avg-bandwidth-range2" type="xs:float" use="optional"/>
+ <xs:attribute name="avg-bandwidth-range3" type="xs:float" use="optional"/>
+ <xs:attribute name="avg-bandwidth-range4" type="xs:float" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="url" type="xs:anyURI" use="required">
+ <xs:annotation>
+ <xs:documentation>The URL (actually, it may be more a URI, but hey, so what) is the access URL to the gridftp server. Each pool may have multiple gridftp servers, or run multiple versions of Globus on different ports.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="storage" type="xs:string" use="optional" default="/">
+ <xs:annotation>
+ <xs
:documentation>This element is the storage mount point prefix. Of course, this may get turned over into other things, augmented by user and system requirements etc. I believe that default works quite well for default Globus setups. </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="GlobusVersionInfo"/>
+ <xs:attribute name="total-size" type="xs:decimal" use="optional"/>
+ <xs:attribute name="free-size" type="xs:decimal" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="jobmanager">
+ <xs:annotation>
+ <xs:documentation>Each pool supports various (usually two) jobmanagers.</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="universe" type="xs:token" use="required">
+ <xs:annotation>
+ <xs:documentation>The universe name is actually the primary key for the jobmanager identification.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attribute name="url"
type="xs:string" use="required">
+ <xs:annotation>
+ <xs:documentation>The contact string is the secondary key for any job manager.</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
+ <xs:attributeGroup ref="GlobusVersionInfo">
+ <xs:annotation>
+ <xs:documentation>Any pool may have multiple versions of Globus installed, and these versions may have multiple jobmanagers listening on different ports.</xs:documentation>
+ </xs:annotation>
+ </xs:attributeGroup>
+ <xs:attribute name="free-mem" type="xs:string" use="optional"/>
+ <xs:attribute name="total-mem" type="xs:string" use="optional"/>
+ <xs:attribute name="max-count" type="xs:string" use="optional"/>
+ <xs:attribute name="max-cpu-time" type="xs:string" use="optional"/>
+ <xs:attribute name="running-jobs" type="xs:nonNegativeInteger" use="optional"/>
+ <xs:attribute name="jobs-in-queue" type="xs:nonNegativeInteger" use="optional"/>
+ <xs:attribute name="idle-nodes" type="xs:n
onNegativeInteger" use="optional"/>
+ <xs:attribute name="total-nodes" type="xs:nonNegativeInteger" use="optional"/>
+ <xs:attribute name="os" type="xs:string" use="optional"/>
+ <xs:attribute name="arch" type="xs:string" use="optional"/>
+ <xs:attribute name="type" type="xs:string" use="optional"/>
+ <xs:attribute name="subnet" type="xs:string" use="optional"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="env">
+ <xs:annotation>
+ <xs:documentation>Allows specifying an environment variable that all applications on this site will see</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:attribute name="key" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="application">
+ <xs:annotation>
+ <xs:documentation>Specifies the details of an application installed on a site</xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:sequence maxOccurs="unbounded">
+ <xs:element ref="profile"
minOccurs="0" maxOccurs="unbounded"/>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="executable" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
\ No newline at end of file
Deleted: branches/faster/src/org/globus/swift/catalog/site/Parser.java
===================================================================
--- branches/faster/src/org/globus/swift/catalog/site/Parser.java 2013-02-24 05:21:18 UTC (rev 6313)
+++ branches/faster/src/org/globus/swift/catalog/site/Parser.java 2013-02-27 03:22:36 UTC (rev 6314)
@@ -1,55 +0,0 @@
-//----------------------------------------------------------------------
-//This code is developed as part of the Java CoG Kit project
-//The terms of the license can be found at http://www.cogkit.org/license
-//This message may not be removed or altered.
-//----------------------------------------------------------------------
-
-/*
- * Created on Jan 7, 2013
- */
-package org.globus.swift.catalog.site;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-
-import javax.xml.XMLConstants;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
-import javax.xml.validation.Validator;
-
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-public class Parser {
- public static final String SCHEMA_RESOURCE = "swift-sites-1.0.xsd";
-
- private File src;
-
- public Parser(String fileName) {
- this.src = new File(fileName);
- }
-
- public Document parse() throws ParserConfigurationException, SAXException, IOException {
- URL schemaURL = Parser.class.getClassLoader().getResource(SCHEMA_RESOURCE);
-
- if (schemaURL == null) {
- throw new IllegalStateException("Sites schema not found in resources: " + SCHEMA_RESOURCE);
- }
-
- SchemaFactory sfactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
- Schema schema = sfactory.newSchema(schemaURL);
-
- DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
- dfactory.setSchema(schema);
- DocumentBuilder dbuilder = dfactory.newDocumentBuilder();
- Document doc = dbuilder.parse(src);
-
- return doc;
- }
-
-}
Added: branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java
===================================================================
--- branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java (rev 0)
+++ branches/faster/src/org/globus/swift/catalog/site/SiteCatalogParser.java 2013-02-27 03:22:36 UTC (rev 6314)
@@ -0,0 +1,93 @@
+//----------------------------------------------------------------------
+//This code is developed as part of the Java CoG Kit project
+//The terms of the license can be found at http://www.cogkit.org/license
+//This message may not be removed or altered.
+//----------------------------------------------------------------------
+
+/*
+ * Created on Jan 7, 2013
+ */
+package org.globus.swift.catalog.site;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+public class SiteCatalogParser {
+ public static final Logger logger = Logger.getLogger(SiteCatalogParser.class);
+
+ public static final String SCHEMA_RESOURCE = "swift-sites-2.0.xsd";
+ static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+
+ private File src;
+
+ public SiteCatalogParser(String fileName) {
+ this.src = new File(fileName);
+ }
+
+ public Document parse() throws ParserConfigurationException, SAXException, IOException {
+ URL schemaURL = SiteCatalogParser.class.getClassLoader().getResource(SCHEMA_RESOURCE);
+
+ if (schemaURL == null) {
+ throw new IllegalStateException("Sites schema not found in resources: " + SCHEMA_RESOURCE);
+ }
+
+ SchemaFactory sfactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ Schema schema = sfactory.newSchema(schemaURL);
+
+ DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
+ dfactory.setNamespaceAware(true);
+ dfactory.setSchema(schema);
+
+ DocumentBuilder dbuilder = dfactory.newDocumentBuilder();
+ dbuilder.setErrorHandler(new CErrorHandler());
+ Document doc = dbuilder.parse(src);
+
+ return doc;
+ }
+
+ private class CErrorHandler implements ErrorHandler {
+
+ @Override
+ public void warning(SAXParseException e) throws SAXException {
+ print(e, "Warning", false);
+ }
+
+ @Override
+ public void error(SAXParseException e) throws SAXException {
+ print(e, "Error", true);
+ }
+
+ @Override
+ public void fatalError(SAXParseException e) throws SAXException {
+ print(e, "Fatal", true);
+ }
+
+ private void print(SAXParseException e, String header, boolean err) {
+ String msg = "[" + header + "] " + src.getName() + ", line " +
+ e.getLineNumber() + ", col " + e.getColumnNumber() + ": " + e.getMessage();
+ if (err) {
+ System.err.println(msg);
+ }
+ else {
+ System.out.println(msg);
+ }
+ if (logger.isInfoEnabled()) {
+ logger.info(msg);
+ }
+ }
+ }
+}
Modified: branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java
===================================================================
--- branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java 2013-02-24 05:21:18 UTC (rev 6313)
+++ branches/faster/src/org/griphyn/vdl/karajan/lib/SiteCatalog.java 2013-02-27 03:22:36 UTC (rev 6314)
@@ -26,7 +26,7 @@
import org.globus.cog.karajan.compiled.nodes.functions.AbstractSingleValuedFunction;
import org.globus.cog.karajan.util.BoundContact;
import org.globus.cog.karajan.util.ContactSet;
-import org.globus.swift.catalog.site.Parser;
+import org.globus.swift.catalog.site.SiteCatalogParser;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
@@ -34,6 +34,10 @@
public class SiteCatalog extends AbstractSingleValuedFunction {
private ArgRef<String> fileName;
+
+ private enum Version {
+ V1, V2;
+ }
@Override
protected Param[] getParams() {
@@ -43,7 +47,7 @@
@Override
public Object function(Stack stack) {
String fn = fileName.getValue(stack);
- Parser p = new Parser(fn);
+ SiteCatalogParser p = new SiteCatalogParser(fn);
try {
Document doc = p.parse();
return buildResources(doc);
@@ -54,27 +58,68 @@
}
private Object buildResources(Document doc) {
+ Node root = getRoot(doc);
+
+ if (root.getLocalName().equals("config")) {
+ return parse(root, Version.V1);
+ }
+ else if (root.getLocalName().equals("sites")) {
+ return parse(root, Version.V1);
+ }
+ else {
+ throw new IllegalArgumentException("Illegal sites file root node: " + root.getLocalName());
+ }
+ }
+
+
+
+ private Object parse(Node config, Version v) {
ContactSet cs = new ContactSet();
- NodeList pools = doc.getElementsByTagName("config").item(0).getChildNodes();
+ NodeList pools = config.getChildNodes();
for (int i = 0; i < pools.getLength(); i++) {
- try {
- BoundContact bc = pool(pools.item(i));
- if (bc != null) {
- cs.addContact(bc);
+ Node n = pools.item(i);
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ try {
+ BoundContact bc = pool(n, v);
+ if (bc != null) {
+ cs.addContact(bc);
+ }
}
+ catch (Exception e) {
+ throw new ExecutionException(this, "Invalid site entry '" + poolName(n, v) + "': ", e);
+ }
}
- catch (Exception e) {
- throw new ExecutionException(this, "Invalid pool entry '" + attr(pools.item(i), "handle") + "': ", e);
- }
}
return cs;
}
- private BoundContact pool(Node n) throws InvalidProviderException, ProviderMethodException {
+ private String poolName(Node site, Version v) {
+ if (site.getLocalName().equals("pool")) {
+ return attr(site, "handle");
+ }
+ else if (site.getLocalName().equals("site")) {
+ return attr(site, "name");
+ }
+ else {
+ throw new IllegalArgumentException("Invalid node: " + site.getLocalName());
+ }
+ }
+
+ private Node getRoot(Document doc) {
+ NodeList l = doc.getChildNodes();
+ for (int i = 0; i < l.getLength(); i++) {
+ if (l.item(i).getNodeType() == Node.ELEMENT_NODE) {
+ return l.item(i);
+ }
+ }
+ throw new IllegalArgumentException("Missing root element");
+ }
+
+ private BoundContact pool(Node n, Version v) throws InvalidProviderException, ProviderMethodException {
if (n.getNodeType() != Node.ELEMENT_NODE) {
return null;
}
- String name = attr(n, "handle");
+ String name = poolName(n, v);
BoundContact bc = new BoundContact(name);
String sysinfo = attr(n, "sysinfo", null);
@@ -91,10 +136,10 @@
}
String ctype = c.getNodeName();
- if (ctype.equals("gridftp")) {
+ if (v == Version.V1 && ctype.equals("gridftp")) {
bc.addService(gridftp(c));
}
- else if (ctype.equals("jobmanager")) {
+ else if (v == Version.V1 && ctype.equals("jobmanager")) {
bc.addService(jobmanager(c));
}
else if (ctype.equals("execution")) {
@@ -115,13 +160,19 @@
else if (ctype.equals("profile")) {
profile(bc, c);
}
+ else if (v == Version.V2 && ctype.equals("application")) {
+ application(bc, c);
+ }
else {
- System.err.println("Unknown node type: " + ctype);
+ throw new IllegalArgumentException("Unknown node type: " + ctype);
}
}
return bc;
}
+ private void application(BoundContact bc, Node c) {
+ }
+
private Service jobmanager(Node n) throws InvalidProviderException, ProviderMethodException {
String provider;
String url = attr(n, "url");
@@ -163,6 +214,9 @@
String provider = attr(n, "provider");
String url = attr(n, "url", null);
String jobManager = attr(n, "jobManager", null);
+ if (jobManager == null) {
+ jobManager = attr(n, "jobmanager", null);
+ }
ExecutionService s = new ExecutionServiceImpl();
s.setProvider(provider);
@@ -230,7 +284,7 @@
throw new IllegalArgumentException("Missing " + name);
}
else {
- return attr.getNodeValue();
+ return expandProps(attr.getNodeValue());
}
}
else {
@@ -246,11 +300,47 @@
return defVal;
}
else {
- return attr.getNodeValue();
+ return expandProps(attr.getNodeValue());
}
}
else {
return defVal;
}
}
+
+ private String expandProps(String v) {
+ if (v == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ int li = -1;
+ for (int i = 0; i < v.length(); i++) {
+ char c = v.charAt(i);
+ switch (c) {
+ case '{':
+ if (li != -1) {
+ li = -1;
+ sb.append('{');
+ }
+ else {
+ li = i;
+ }
+ break;
+ case '}':
+ if (li != -1) {
+ sb.append(System.getProperty(v.substring(li + 1, i)));
+ li = -1;
+ }
+ else {
+ sb.append(c);
+ }
+ break;
+ default:
+ if (li == -1) {
+ sb.append(c);
+ }
+ }
+ }
+ return sb.toString();
+ }
}
More information about the Swift-commit
mailing list