August 04, 2008

XML Xpath

クエリの頭が"//"の場合は絶対パス、無い場合は相対パスとなる。相対パスを使用する時はevaluate()メソッドの第2引数に基点となるノードを渡す。

全てのfoo要素
$items = $xpath->evaluate("//foo");
$nodeの子要素のfoo要素(孫以下は含まれない)
$items = $xpath->evaluate("foo", $node);

属性は"@属性名"で指定する。

attr属性を持つfoo要素
$items = $xpath->evaluate("//foo[@attr]");
attr属性の値が'test'のfoo要素
$items = $xpath->evaluate("//foo[@attr = 'test']");
attr属性の値が'http://'で始まるfoo要素
$items = $xpath->evaluate("//foo[starts-with(@attr, 'http://')]");
attr属性の値が'http://'で始まらないfoo要素
$items = $xpath->evaluate("//foo[not(starts-with(@attr, 'http://'))]");
attr属性の値が'http://'で始り、attr2属性の値が'hoge'のfoo要素
$items = $xpath->evaluate("//foo[starts-with(@attr, 'http://') and attr2 = 'hoge']");
attr属性の値に'://'を含むfoo要素
$items = $xpath->evaluate("//foo[contains(@attr, '://')");

テキストノード(の内容)はtext()で指定する。

テキストが'test'のfoo要素。
$items = $xpath->evaluate("//foo[text() = 'test']");
テキストが'test'でないfoo要素。
$items = $xpath->evaluate("//foo[text() != 'test']");

users.xml
<?xml version="1.0" encoding="utf-8"?>
<users>
  <user id="1">
    <name>satou</name>
    <age>20</age>
  </user>
  <user id="2">
    <name>tanaka</name>
    <age>30</age>
  </user>
  <user id="3">
    <name>suzuki</name>
    <age>40</age>
  </user>
</users>
ageの値(テキスト)が30以上のuser要素
$items = $xpath->evaluate("user[age/text() >= 30]", $users);
var_dump($item->length);  // int(2)
ageが30以上、かつnameが'suzuki'のuser要素
$items = $xpath->evaluate("user[age/text() >= 30 and name/text() = 'suzuki']", $users);
var_dump($item->length);  // int(1)


July 29, 2008

SWT Browser Widget

HTMLをレンダリングするJavaクライアントアプリを作成する。

JDKをインストールする。
http://java.sun.com/javase/downloads/index.jsp

Eclipse(Eclipse IDE for Java EE Developers)をダウンロードする。
http://www.eclipse.org/downloads/

SWTをダウンロードする。
http://www.eclipse.org/swt/

SWT(zip)をC:\java\swttestに解凍する。

JAVA_HOME\binを環境変数PATHに追加する。
C:\Program Files\Java\jdk1.6.0_07\bin
Eclipseに含まれるSWTを展開する。
C:\HOME>cd C:\ECLIPSE_HOME\plugins
C:\ECLIPSE_HOME\plugins>jar xvf org.eclipse.swt.win32.win32.x86_x.x.x.vxxxxf.jar
展開して出てきた.dllファイルをC:\java\swttestに移動する。

BrowsetTestクラスを作成する。
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.browser.*;

public class BrowserTest
{
  public static void main(String args[])
  {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setSize(800, 600);
    shell.open();
    
    Browser browser = new Browser(shell, SWT.NONE);
    browser.setBounds(shell.getClientArea());
    browser.setUrl("http://www.google.com/");
    
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
    
    display.dispose();
  }
}
コンパイル & 実行
C:\java\swttest>javac -classpath swt.jar;. BrowserTest.java
C:\java\swttest>java -classpath swt.jar;. BrowserTest


July 21, 2008

PHP - XML(3)

XMLにおける、複数のネームスペースと複数のスキーマによるバリデーションの考察。

user要素は以下のようなprofile要素(型)を持つとする。
<profile>
  <age>18</age>
  <prefecture>Tokyo</prefecture>
</profile>
このprofile型は他でも使えるかもしれない、という場合、profile型を定義するスキーマ"profile.xsd"を作成する。この時、このprofile型はネームスペース"prf"に属することとする。

profile.xsdにはprofile型(複合型)、age型(単純型)、prefecture型(単純型)が含まれるため、それらを宣言する。
<element name="profile" type="prf:PROFILE_CTYPE" />
<element name="age" type="prf:PROFILE_AGE_STYPE" />
<element name="prefecture" type="prf:PROFILE_PREFECTURE_STYPE" />
単純型の定義を行う。
<simpleType name="PROFILE_AGE_STYPE">
  <restriction base="integer">
    <minInclusive value="18" />
    <maxInclusive value="120" />
  </restriction>
</simpleType>

<simpleType name="PROFILE_PREFECTURE_STYPE">
  <restriction base="string" />
</simpleType>
定義した単純型を参照し、profile型を定義する。
<complexType name="PROFILE_CTYPE">
  <sequence>
    <element ref="prf:age" />
    <element ref="prf:prefecture" />
  </sequence>
</complexType>
出来上がったprofile.xsdの全体。
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://mydomain/profns"
        xmlns="http://www.w3.org/2001/XMLSchema"
        xmlns:prf="http://mydomain/profns">
  
  <element name="profile" type="prf:PROFILE_CTYPE" />
  <element name="age" type="prf:PROFILE_AGE_STYPE" />
  <element name="prefecture" type="prf:PROFILE_PREFECTURE_STYPE" />
  
  <simpleType name="PROFILE_AGE_STYPE">
    <restriction base="integer">
      <minInclusive value="18" />
      <maxInclusive value="120" />
    </restriction>
  </simpleType>
  
  <simpleType name="PROFILE_PREFECTURE_STYPE">
    <restriction base="string" />
  </simpleType>
  
  <complexType name="PROFILE_CTYPE">
    <sequence>
      <element ref="prf:age" />
      <element ref="prf:prefecture" />
    </sequence>
  </complexType>
</schema>
XMLでuserは以下のように記述される(プロフィールは除外してある)。
<user>
  <id>1</id>
  <registeredDate>2008-01-01T10:00:00</registeredDate>
</user>
userはネームスペース"usr"に属することとした場合、XMLは次のように変更される。
<usr:user>
  <usr:id>1</usr:id>
  <usr:registeredDate>2008-01-01T10:00:00</usr:registeredDate>
</usr:user>
ネームスペース"usr"のスキーマ定義を行う。
<element name="users" type="usr:USERS_CTYPE" />
<element name="user" type="usr:USER_CTYPE" />
<element name="id" type="usr:USER_ID_STYPE" />
<element name="registeredDate" type="usr:USER_REGISTERED_DATE_STYPE" />

<complexType name="USERS_CTYPE">
  <sequence>
    <element ref="usr:user" minOccurs="1" maxOccurs="unbounded" />
  </sequence>
</complexType>

<simpleType name="USER_ID_STYPE">
  <restriction base="integer" />
</simpleType>

<simpleType name="USER_REGISTERED_DATE_STYPE">
  <restriction base="dateTime" />
</simpleType>

<complexType name="USER_CTYPE">
  <sequence>
    <element ref="usr:id" />
    <element ref="usr:registeredDate" />
  </sequence>
</complexType>
次に、userはprofileを持つという定義の実装を行う。profile型は外部(profile.xsd)で定義されているため、それをインポートする。
<import namespace="http://mydomain/profns"
        schemaLocation="http://mydomain/xmlSchema/profile.xsd" />
インポートするとprofile型が参照できるようになる。
userにprofile要素を追加する。
<complexType name="USER_CTYPE">
  <sequence>
    ...
    <element ref="prf:profile" />
  </sequence>
</complexType>
出来上がったuser.xsdの全体。
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://mydomain/userns"
        xmlns="http://www.w3.org/2001/XMLSchema"
        xmlns:usr="http://mydomain/userns"
        xmlns:prf="http://mydomain/profns">
  
  <import namespace="http://mydomain/profns"
          schemaLocation="http://mydomain/xmlSchema/profile.xsd" />
  
  <element name="users" type="usr:USERS_CTYPE" />
  <element name="user" type="usr:USER_CTYPE" />
  <element name="id" type="usr:USER_ID_STYPE" />
  <element name="registeredDate" type="usr:USER_REGISTERED_DATE_STYPE" />
  
  <complexType name="USERS_CTYPE">
    <sequence>
      <element ref="usr:user" minOccurs="1" maxOccurs="unbounded" />
    </sequence>
  </complexType>
  
  <simpleType name="USER_ID_STYPE">
    <restriction base="integer" />
  </simpleType>
  
  <simpleType name="USER_REGISTERED_DATE_STYPE">
    <restriction base="dateTime" />
  </simpleType>
  
  <complexType name="USER_CTYPE">
    <sequence>
      <element ref="usr:id" />
      <element ref="usr:registeredDate" />
      <element ref="prf:profile" />
    </sequence>
  </complexType>
</schema>
これらのスキーマ定義に沿うXMLの例。
<?xml version="1.0" encoding="utf-8"?>
<usr:users xmlns:usr="http://mydomain/userns"
           xmlns:prf="http://mydomain/profns"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://mydomain/xmlSchema/user.xsd">
  <usr:user>
    <usr:id>1</usr:id>
    <usr:registeredDate>2008-01-01T10:00:00</usr:registeredDate>
    <prf:profile>
      <prf:age>20</prf:age>
      <prf:prefecture>Tokyo</prf:prefecture>
    </prf:profile>
  </usr:user>
  <usr:user>
    <usr:id>2</usr:id>
    <usr:registeredDate>2008-02-02T15:00:00</usr:registeredDate>
    <prf:profile>
      <prf:age>30</prf:age>
      <prf:prefecture>Aichi</prf:prefecture>
    </prf:profile>
  </usr:user>
</usr:users>
バリデーションのコード例。
$doc = new DOMDocument();
$doc->loadXML(file_get_contents("/path/to/users.xml"));

$xpath = new DOMXPath($doc);
$res = $xpath->evaluate("//@xsi:schemaLocation");
$doc->schemaValidate($res->item(0)->nodeValue);
試しに年齢を200にするとWarningが発生する。スキーマ定義やprofile.xsdのインポートなど、機能していることがわかる。
Warning: DOMDocument::schemaValidate() [function.DOMDocument-schemaValidate]:
  Element '{http://mydomain/profns}age': [facet 'maxInclusive'] The value '200' is greater than the maximum value allowed ('120').
  in /usr/local/www/data/sabel/app/index/controllers/Index.php on line 17


July 20, 2008

PHP - XML(2)

XMLにおけるスキーマ定義の基本的な検証。

以下のようなXMLの有効性を検証するスキーマ定義を作成する。
<?xml version="1.0" encoding="utf-8"?>
<users xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://mydomain/xmlSchema/user.xsd">
  <user>
    <id>1</id>
    <registeredDate>2008-01-01T10:00:00</registeredDate>
  </user>
  <user>
    <id>2</id>
    <registeredDate>2008-02-02T15:00:00</registeredDate>
  </user>
  <user>
    <id>3</id>
    <registeredDate>2008-03-03T20:00:00</registeredDate>
  </user>
</users>
まず、複合型となる、ドキュメント要素のusersと子要素のuserを定義する。
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="users" type="DEF_USERS" />
  <xsd:element name="user" type="DEF_USER" />
  
  <xsd:complexType name="DEF_USERS">
  </xsd:complexType>
  
  <xsd:complexType name="DEF_USER">
  </xsd:complexType>
</xsd:schema>
次に、usersにuserが1つ以上含まれることを定義する。x以上はminOccursで、x以下はmaxOccursで指定する。最大値がいくらでも良い場合はunboundedとする。
  ...
  
  <xsd:complexType name="DEF_USERS">
    <xsd:sequence>
      <xsd:element ref="user" minOccurs="1" maxOccurs="unbounded" />
    </xsd:sequence>
  </xsd:complexType>
  
  ...
次に、userが持つ単純型の要素(idやregisteredDate)の定義を行う。typeで、idは数値型、registeredDateは日付時刻型であることを示す。データ型はstring, boolean, floatなど色々ある。こちらを参照。
  ...
  
  <xsd:complexType name="DEF_USER">
    <xsd:sequence>
      <xsd:element name="id" type="xsd:int" />
      <xsd:element name="registeredDate" type="xsd:dateTime" />
    </xsd:sequence>
  </xsd:complexType>
  
  ...
できあがったスキーマ定義でバリデーションを行う。
$doc = new DOMDocument();
$doc->loadXML(file_get_contents("/path/to/users.xml"));

$xpath = new DOMXPath($doc);
$res = $xpath->evaluate("//@xsi:schemaLocation");
$doc->schemaValidate($res->item(0)->nodeValue);
userのidの値を"abc"に変更するとWarningが発生する。バリデーションが正常に機能していることが分かる。
Warning: DOMDocument::schemaValidate() [function.DOMDocument-schemaValidate]:
  Element 'id': 'abc' is not a valid value of the atomic type 'xs:int'.
  in /usr/local/www/data/sabel/app/index/controllers/Index.php on line 16
userを0個にした時のエラー。
Warning: DOMDocument::schemaValidate() [function.DOMDocument-schemaValidate]:
  Element 'users': Missing child element(s). Expected is ( user ).
  in /usr/local/www/data/sabel/app/index/controllers/Index.php on line 16
userがageを持つように変更。しかし、ageはなくても良いものとする。また、値は整数で18〜120とする(18や120という閾値を含まない場合、minExclusiveやmaxExclusiveにする)。この場合、データ型にintegerを指定するだけでは十分なルールでないため、年齢型を作成する。
  ...
  
  <xsd:simpleType name="DEF_AGE">
    <xsd:restriction base="xsd:integer">
      <xsd:minInclusive value="18" />
      <xsd:maxInclusive value="120" />
    </xsd:restriction>
  </xsd:simpleType>
  
  ...
userにageの定義を追加する。データ型には作成したDEF_AGEを指定し、必須要素ではないためminOccursで0を指定する。
  ...
  
  <xsd:complexType name="DEF_USER">
    <xsd:sequence>
      ...
      <xsd:element name="age" type="DEF_AGE" minOccurs="0" />
    </xsd:sequence>
  </xsd:complexType>
  
  ...
これでuserにageを持たせられるようになった。不正な値、例えば121とすると以下のようなエラーになる。
Warning: DOMDocument::schemaValidate() [function.DOMDocument-schemaValidate]:
  Element 'age': [facet 'maxInclusive'] The value '121' is greater than the maximum value allowed ('120').
  in /usr/local/www/data/sabel/app/index/controllers/Index.php on line 16
今回作成したスキーマ定義の全体(user.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="users" type="DEF_USERS" />
  <xsd:element name="user" type="DEF_USER" />
  
  <xsd:complexType name="DEF_USERS">
    <xsd:sequence>
      <xsd:element ref="user" minOccurs="1" maxOccurs="unbounded" />
    </xsd:sequence>
  </xsd:complexType>
  
  <xsd:simpleType name="DEF_AGE">
    <xsd:restriction base="xsd:integer">
      <xsd:minExclusive value="18"/>
      <xsd:maxInclusive value="120"/>
    </xsd:restriction>
  </xsd:simpleType>
  
  <xsd:complexType name="DEF_USER">
    <xsd:sequence>
      <xsd:element name="id" type="xsd:int" />
      <xsd:element name="registeredDate" type="xsd:dateTime" />
      <xsd:element name="age" type="DEF_AGE" minOccurs="0" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>


July 19, 2008

PHP - XML

XMLにおけるネームスペースの基本的な検証。

以下のようなXMLがある。
<?xml version="1.0" encoding="utf-8"?>
<books>
  <book>
    <title>book title1</title>
    <author>author name1</author>
  </book>
  <book>
    <title>book title2</title>
    <author>author name2</author>
  </book>
</books>
タグ名による要素の取得。
$doc = new DOMDocument();
$doc->loadXML(file_get_contents("/path/to/test.xml"));
$books = $doc->documentElement;
$titles = $books->getElementsByTagName("title");
var_dump($titles->length);  // int(2)
bookの1つ目と2つ目に、それぞれネームスペースを与える。ネームスペースのURIは単なる名前で、存在するリソースを示さなくても良い。
<?xml version="1.0" encoding="utf-8"?>
<books>
  <book1:book xmlns:book1="http://www.example.com/ns1">
    <book1:title>book title1</book1:title>
    <book1:author>author name1</book1:author>
  </book1:book>
  <book2:book xmlns:book2="http://www.example.com/ns2">
    <book2:title>book title2</book2:title>
    <book2:author>author name2</book2:author>
  </book2:book>
</books>
ネームスペース名(URI)を指定した、タグ名による要素の取得をする。
$doc = new DOMDocument();
$doc->loadXML(file_get_contents("/path/to/test.xml"));
$books = $doc->documentElement;

$titles = $books->getElementsByTagName("title");
var_dump($titles->length);  // int(2)

$titles = $books->getElementsByTagNameNS("http://www.example.com/ns1", "title");
var_dump($titles->length);  // int(1)

$titles = $books->getElementsByTagNameNS("http://www.example.com/ns2", "title");
var_dump($titles->length);  // int(1)
親要素にネームスペースが与えられ、子要素のネームスペースが省略されている場合は、子要素は親要素のネームスペースに属することになる。この時、プレフィックスは省略できる。これは先程のXML文書と同等のものとなる。
<?xml version="1.0" encoding="utf-8"?>
<books>
  <book xmlns="http://www.example.com/ns1">
    <title>book title1</title>
    <author>author name1</author>
  </book>
  <book xmlns="http://www.example.com/ns2">
    <title>book title2</title>
    <author>author name2</author>
  </book>
</books>
子要素がさらに別のネームスペースに属する場合、親要素のネームスペースはそれ以上下位に適用されない。
<?xml version="1.0" encoding="utf-8"?>
<books>
  <book xmlns="http://www.example.com/ns1">
    <title>book title1</title>
    <author>author name1</author>
    <foo xmlns="http://www.example.com/foo">
      <title>foo</title>
    </foo>
  </book>
</books>
$doc = new DOMDocument();
$doc->loadXML(file_get_contents("/path/to/test.xml"));
$books = $doc->documentElement;

$titles = $books->getElementsByTagName("title");
var_dump($titles->length);  // int(2)

$titles = $books->getElementsByTagNameNS("http://www.example.com/ns1", "title");
var_dump($titles->length);  // int(1)