基本的なカスタマイズ > ユーザーインタフェースのカスタマイズ > UI の情報の表示 > Windchill クライアントアーキテクチャツリー > ソリューション
  
ソリューション
ツリー形式で Windchill ビジネスオブジェクトを表示するには、Windchill クライアントアーキテクチャツリーを使用します。
前提となる知識
この目的を達成するには、次のことを理解している必要があります。
Windchill クライアントアーキテクチャでの操作フレームワーク
JCA テーブルコンポーネント
データ取得、データユーティリティ、GUI コンポーネント
JSP、JavaScript、およびカスタム taglib
Java アノテーション
ソリューションエレメント
エレメント
タイプ
説明
<ユア_設定ビルダー>
java
ツリーコンフィギュレーションを定義します。
<ユア_データビルダー>
java
データフェッチサービス/コマンドを定義します。
<ユア_ビューページ>.jsp
jsp
ビューレンダリングロジックを定義します。
<your>service.properties.xconf
xconf
DataUtilities およびサービスを定義します。
<your>action.rbInfo
rbInfo
操作属性を定義します。
<your>actions.xml
xml
操作を定義できます。
<your>actionModels.xml
xml
操作モデルを定義できます。
非同期 DataSource を使用したツリーコンポーネントの実装
非同期 DataSource を使用してツリーコンポーネントを実装するには、TreeDataBuilderAsync インタフェースを実装する必要があります。次の具象クラスによって実装される 1 つのメソッドがあります。
void buildNodeData(Object node, ComponentResultProcessor resultProcessor) throws Exception
各ノードの子をフェッチします。インフラストラクチャからこのメソッドを最初に呼び出すと、必ず 1 つ目の引数が TreeNode.RootNode として渡されます。この最初の呼び出しによって、すべてのルートノードが以降の呼び出しでフェッチされます。以降の呼び出しでは、子ノードをフェッチする必要があるツリーノードが必ず渡されます。
例として、この事例を処理するコードの抜粋を次に示します。
List nodes;
if (node == TreeNode.RootNode){
nodes = getRootNodes();
resultProcessor.addElements(nodes);
}else {
getNodes(node);
nodes = resultProcessor.addElements(nodes);
}
DataSource を使用しないツリーコンポーネントの実装
DataSource を使用しないでツリーコンポーネントを実装するには、TreeHandler インタフェースを実装する必要があります。TreeHandlerAdapter はデフォルトの実装を提供します。TreeHandler は JAVA インタフェースであり、ツリーコンテンツの作成を行います。次のメソッドは TreeHandler によって提供されます。
ModelContext getModelContext(): - ハンドラのモデルコンテキストを取得します。
void setModelContext(ModelContext mc) throws WTException: - 記述子やコマンド Bean など作成されるツリーのコンテキスト情報を設定します。このメソッドは、ハンドラからデータが要求される前に初期化する必要があります。
List getRootNodes() throws WTException: - ツリーのルートノードのリストを取得します。
Map<Object,List> getNodes(List parents) throws WTException: - 指定されたリストの親ノードごとに子ノードのマッピングを取得します。このメソッドは、展開操作で呼び出される唯一のメソッドで、親と子の組み合わせ情報を提供できるようにハンドラを適切に初期化できる必要があります。
boolean isExpandNeeded(Object node, int level) throws WTException: - 指定されたノードを展開する必要があるかどうかを指定します。デフォルトの実装は、タグによってすでに展開または設定されているノードのリストのセッション状態を参照します。
boolean hasChildren(Object node) throws WTException: - 指定されたノードが折りたたまれている場合、このノードに展開ボタンを表示するかどうかを指定します。パフォーマンスを改善する場合や、カスタム動作を取得する場合は、このメソッドをオーバーライドします。デフォルトの実装は、getNodes を呼び出し、サイズが 0 より大きいかどうかを確認します。
void addExpandedNode(Object node) throws WTException: - 展開されたノードのリストにノードを追加します。このリストは、ツリーの展開の内容を識別するために返され、セッション状態で使用されます。
ツリーは、データベースへの多くの照会を生じる再帰的な性質があるので、注意深く作成する必要があります。Treehandler クラスはクラスのプライベート属性としてツリー状態情報を保存し、getNodes 呼び出しごとに再使用されます。また、isExpandNeeded メソッドを使用して、ユーザーが展開したツリーの行を識別できます。
TreeHandlerAdapter は、拡張可能な TreeHandler を実装する抽象クラスです。以下に例を示します。
getRootNodes() は、ルートノードの内容を指定します。TreeHandler は、必要な情報にアクセスするために使用する ModelContext にアクセスできます。
public List getRootNodes() throws WTException {
NmCommandBean cb = getModelContext().getNmCommandBean();
WTPart part;
NmOid oid = cb.getPageOid();
if (oid == null) {
log.debug("No oid found in request, trying GOLF_CART");
part = getGolfCart();
}else {
if (!oid.isA(WTPart.class)) {
throw new ClassCastException("Expected part, but was: " + oid);
}
part = (WTPart)oid.getRef();
}
if (part == null) {
log.debug("Couldn't get part");
return null;
}

configSpec = ConfigHelper.service.getConfigSpecFor(part);
return Collections.singletonList(part);
}
メソッドは、最初にモデルコンテキストから NmCommandBean を読み込み、この NmCommandBean からプライマリ OID を抽出します。ユーザーが OID を要求しない場合、getGolfCart メソッドを呼び出すことによって、代わりに GOLF_CART 部品を使用します。getGolfCart メソッドは照会を実行して GOLF_CART を返すだけです。ユーザーがリクエストパラメータで OID を指定した場合は、その OID が WTPart かどうかを確認します。それ以外の場合は、ClassCastException が発生します。予想された OID の場合、getRef メソッドを使用してこの OID から部品が読み込まれ、プロセスは継続されます。このメソッドによって参照オブジェクトがインフレートされ、WTPart に送信されます。OID または GOLF_CART から読み込まれた部品が Null であるかどうかが最後に確認され、Null の場合、Null が返されます。configSpec 変数は、ConfigHelper クラスを使用して、部品の ConfigSpec に割り当てられます。このヘルパークラスには、マスター化オブジェクトから作業版数されたオブジェクトを取得するのに役立つサービスが含まれています。最後に、部品を含む不変のリストが返されます。
getNodes(List parents) 指定されたリストの親ノードごとに子ノードのマッピングを取得します。このメソッドは、最初に getRootNodes() メソッドを呼び出さずに直接呼び出すことができます (展開操作など)。すなわち、このメソッドは、親と子の組み合わせ情報を提供できるようにハンドラを適切に初期化できる必要があります。
この例では、WTPartUsageLinks に基づいて WTPart の階層が生成されます。
public Map<Object,List> getNodes(List parents) throws WTException {
if (configSpec == null) {
configSpec = getDefaultConfigSpec();
}
Map<Object,List> result = new HashMap<Object,List>();
//API returns a 3D array where the 1st dim is the parent parts,
//the 2nd dim is the list of children for a given parent,
//and the 3rd dim is 2 element array w/the link obj at 0 and the child part at 1
Persistable[][][] all_children = WTPartHelper.service.getUsesWTParts(
new WTArrayList(parents), configSpec);
for (ListIterator i = parents.listIterator(); i.hasNext();) {
WTPart parent = (WTPart)i.next();
Persistable[][] branch = all_children[i.previousIndex()];
if (branch == null) {
continue;
}
List children = new ArrayList(branch.length);
result.put(parent,children);
for (Persistable[] child : branch) {
children.add(child[1]);
}
}
log.debug("ParentsToChildren: " + result);
return result;
}
private ConfigSpec getDefaultConfigSpec() throws WTException {
return ConfigHelper.service.getDefaultConfigSpecFor(WTPart.class);
}
カスタムツリーハンドラを登録する必要があります。サービスの登録方法については、<DataUtilities Document> へのリファレンスを参照してください。以下はツリーハンドラエントリの例です。
<Service context="default" name="com.ptc.core.components.beans.TreeHandler">
<Option requestor="your_object" selector="your_treeHandler"
serviceClass="your_treeHandler_class"
cardinality="duplicate"/>
</Service>
ツリーで非永続オブジェクトを表示する場合、getRootNodes および getNodes メソッドの要素を返してください。要素に有効で使用可能な ufid 値または obid 属性が含まれていない場合、行オブジェクトごとに NmObject を指定する必要があります。これには、2 つの方法があります。
1. DefaultNmObjectUtility を拡張する DataUtility を作成し、行オブジェクトの NmObject を返す getTargetObject() メソッドをオーバーライドします。詳細については、NmObject ユーティリティを参照してください。
2. 要素に、値が NmObject のインスタンスの "nmObject" という名前の属性を含めます。
または、NmObject から拡張するラッパークラスを記述し、getOid() メソッドをオーバーライドして適切な NmOid を返すことで、非永続オブジェクトを表示できます。
手順 - JCA ツリーの設定
ComponentConfigBuiler と ComponentDataBuilder の実装
表示の定義
ComponentConfigBuiler と ComponentDataBuilder の実装
以下の 2 つのアプローチがあります。
別個のビルダーによるアプローチ
単一ビルダーによるアプローチ
別個のビルダーによるアプローチ
この場合、ComponentConfigBuiler と ComponentDataBuilder に別々のクラスが記述されます。
非同期の DataSource の使用
ComponentConfigBuilder の実装
例:
@ComponentBuilder(value = "custom.treeExample.seperate", type = ComponentBuilderType.CONFIG_ONLY)
public class TreeExampleConfigBuilder extends AbstractComponentConfigBuilder {
private static final String RESOURCE = "com.ptc.carambola.carambolaResource";
@Override
public ComponentConfig buildComponentConfig(ComponentParams params)
throws WTException {
ComponentConfigFactory factory = getComponentConfigFactory();
//Create TreeConfig
TreeConfig tree = factory.newTreeConfig();
// Need to set DataSOurceModes explicitely to DataSourceMode.ASYNCHRONOUS
((JcaTreeConfig) tree).setDataSourceMode(DataSourceMode.ASYNCHRONOUS);
// Set expansion level . Default is TableTreeProperties.ONE_EXPAND
(expand by one level)
((JcaTreeConfig) tree).setExpansionLevel(TableTreeProperties.FULL_EXPAND);

tree.setLabel((new ResourceBundleClientMessageSource(RESOURCE)).getMessage
("PART_TREE_LABEL"));

//Add Columns to the config
tree.addComponent(factory.newColumnConfig(NAME, true));
tree.addComponent(factory.newColumnConfig(NUMBER, true));
//Set column to which expand/collapse norgie should appear
tree.setNodeColumn(NUMBER);
return tree;
}
}
要点:
1. 非同期 DataSource を使用するには、DataSource モードを明示的に DataSourceMode.ASYNCHRONOUS に設定する必要があります。
2. setNodeColumn メソッドによって、展開/折りたたみボタンを表示する列を選択できます。デフォルトは "名前" 列です。
ComponentDataBuilder の実装
具象クラスによってインタフェース com.ptc.mvc.components.TreeDataBuilderAsync を実装する必要があります。詳細については、Java API のマニュアルを参照してください。
例:
@ComponentBuilder(value = "custom.treeExample.seperate", type =
ComponentBuilderType.DATA_ONLY)
public class TreeExampleComponentDataBuilder
implements TreeDataBuilderAsync {
@Override
public void buildNodeData(Object node,
ComponentResultProcessor resultProcessor)
throws Exception {
if (node == TreeNode.RootNode) {
List<Object> objects = getRootNodes();
resultProcessor.addElements(objects);
} else {
List nodeList = new ArrayList();
nodeList.add(node);
Map<Object, List> map = getNodes(nodeList);
Set keySet = map.keySet();
for (Object key : keySet) {
resultProcessor.addElements(map.get(key));
}
}
}
private List<Object> getRootNodes(){ // Add code to find RootNodes}
private List<Object> getNodes(List<Object> nodeList){ // Add code to find ChildNodes}
}
TreeExpansionStateManager を実装することによる高度なコンフィギュレーション
TreeExpansionStateManager は以下の API を提供します。
boolean isExpandNeeded(DefaultMutableTreeNode node, ComponentConfig config,
ComponentParams params) throws WTException;
void addExpandedNode(Object node, ComponentConfig config,
ComponentParams params)
throws WTException;

Set getExpandedOids(ComponentConfig config, ComponentParams params)
throws WTException;
boolean hasChildren(Object node, ComponentResultProcessor resultProcessor,
TreeDataBuilderAsync builder) throws Exception;
List<DefaultMutableTreeNode> getDynamicExpandedNodes(ComponentConfig config,
ComponentParams params) throws WTException;
詳細については、Javadoc を参照してください。
TreeExpansionStateManager の具体化は DefaultTreeExpansionStateManager によって提供されます。この実装を拡張して必要に応じて API をカスタマイズしたり、TreeExpansionStateManager を直接実装したりすることができます。ExpansionStateManager アノテーションを使用して、具体化をビルダー内に注入できます。
例:
@ComponentBuilder ("folderbrowser_tree")
@ExpansionStateManager (FolderTreeExpansionStateHandler.class)
//
public class FolderTreeBuilder extends AbstractComponentConfigBuilder implements TreeDataBuilderAsync{…}
ここでは FolderTreeExpansionStateHandlerDefaultTreeExpansionStateManager を実装し、FolderTreebuilder に注入されます。
これらの API はツリー拡張状態の動作をカスタマイズするのに便利です。たとえば、IsExpandNeeded API を使用して、ノードを拡張するかどうかを決定するデフォルトのロジックをオーバーライドできます。
DataSource を使用しない場合
この場合、setDataSourceMethod を呼び出さないようにするか、dataSourceModeDataSourceMode.SYNCHRONOUS に設定する必要があります。その他すべてのコンフィギュレーションは、「ComponentConfigBuilder の実装」のセクションでの説明と同じです。
ComponentConfigBuilder の実装
ComponentDataBuilder の実装
この場合、databuilder によって ComponentDataBuilder インタフェースを実装する必要があります。
例:
@ComponentBuilder(value = "custom.treeExample.seperate", type = ComponentBuilderType.DATA_ONLY)
public class TreeExampleComponentDataBuilder implements ComponentDataBuilder {
@Override
public TreeHandler buildComponentData(ComponentConfig config, ComponentParams params) throws WTException {
return new customTreeHandler();// customTreeHandler should implement TreeHandler or extend TreeHandlerAdapter
}
}
単一ビルダーによるアプローチ
この場合、単一のクラスによって ComponentConfigBuilder と ComponentDataBuilder の両方が実装されます。
非同期の DataSource の使用
例:
@ComponentBuilder(value = "custom.treeExample")
public class TreeExampleBuilder implements TreeDataBuilderAsync, ComponentConfigBuilder,ComponentConfigFactoryAware {....}
DataSource を使用しない場合
例:
@ComponentBuilder(value = "custom.treeExample")
public class TreeExampleComponentDataBuilder extends AbstractConfigurableTableBuilder{….}
表示の定義
componentConfig の setView メソッドによって、特定のビルダーのビューを設定できます。ビューは /WEB-INF/jsp フォルダを基準にして設定する必要があります。デフォルトビューは WEB-INF/jsp/components/tree.jsp を使用するように指定されています。
ファイル <Windchill>\codebase\WEB-INF\tlds\jcaMvc.tld 内のカスタムタグ定義を使用して、デフォルトのビューをカスタマイズできます。