Navigating Through an Object’s Structure with ObjectSet Application
In import/export, a new optional <loadCondition> element under navigation <rule> element has been added. These rules reside in XML files under <Windchill>\codebase\registry\ixb\object_set_handlers.
The <loadCondition> element will have a fully qualified <className> and <methodName>. Object Navigation framework will call the specified method using reflection. The method must be static and must return boolean and must not have arguments. If the specified method returns a true only then the rule will be loaded. In case <loadCondition> is not present, the rule will be loaded by default.
If the <loadCondition> element is present, the framework first calls the specified method to decide if the rule should be loaded or not.
The element looks like:
<loadCondition>
<className>xxxx</className>
<methodName>yyy</methodName>
</loadCondition>
Example:
<loadCondition>
<className>wt.ixb.objectset.ObjectSetHelper</className>
<methodName>isPDMLinkInstalled</methodName>
</loadCondition>
Object Collection
When an object is given to the export process, the ObjectSet application does the job of navigating through the object's structure and collecting all of its related objects.
The definition of the navigation is taken from a set of XML files known as navigation?rule files. Additionally, the ObjectSet application uses Java classes known as generators and filters to collect a set of objects that will be exported when simple navigation is not enough and some programming logic needs to be applied.
The navigation rule files reside in the folder <Windchill>\codebase\registry\ixb\object_set_handlers.
Navigating Rules
There are two types of navigating rules: Generator and Filter.
• Generators are rules that are used by the application to traverse through the object’s structure and get all of its objects to be exported, such as “uses”, “described by”, “reference”, etc.
• Filters are rules that are applied to objects to be exported to exclude certain objects from the export process. For example, with “Filter By Time”, we can choose to export objects that were modified during a specified period. This is the only OOTB filter currently available.
Available GeneratorIds are defined in XML files in the folder <Windchill>\codebase\registry\ixb\object_set_handlers with the tag <setGenerator>. A list of these Generator Ids can be obtained by calling to IXBHelper.service.getGeneratorList() passing the first argument as false. . The call returns all available Generators in the system.
IXBHelper.service.getGeneratorList() passing the first argument as true returns a list of Generators that will be displayed to the GUI for end-user selection. This helps to hide Generators that you don’t want the end-user to see. To hide these Generators, the XML files for these Generators, should have the <display> tag set to false.
For example: a paragraph of XML file for WTPart from <Windchill>\codebase\registry\ixb\object_set_handlers\product_struct.xml.
<setGenerator>
<id>productStructureNavigator</id>
<handler>
wt.ixb.objectset.handlers.navigator.ProductStructureNavigator
</handler>
<dialogClassName>
wt.clients.ixb.exp.NavigatorSearchDialog
</dialogClassName>
<localizedName>
<localizedString>
<class>wt.ixb.objectset.objectSetResource</class>
<key>PRODUCT_STRUCTURE_NAME</key>
</localizedString>
</localizedName>
Tags
Object Navigation
The mechanism is an XML-rule-driven “navigator” of Windchill objects. Given a seed (top level) object the mechanism uses specified rules to navigate from object to object. The navigation can be performed through a DB link, a foreign key, or a specified method. Here is an example of rule definition of WTPart. The seed is a Folder object.
<Windchill>\codebase\registry\ixb\object_set_handlers\product_struct.xml
<handler>
wt.ixb.objectset.handlers.navigator.ProductStructureNavigator
</handler>
...
<schema>
...
<rule>
<for>wt.part.WTPart</for>
<go>
<byMethod>
<method>navigateFromWTPartToDescribeByDoc</method>
</byMethod>
</go>
</rule>
<rule>
<for>wt.part.WTPartDescribeLink</for>
<go>
<fromForeignKey>
<targetClass>wt.doc.WTDocument</targetClass>
<getMethodName>getDescribedBy</getMethodName>
</fromForeignKey>
</go>
</rule>
...
</schema>
The example above shows both possible types of navigation: From WTPart it navigates to wt.part.WTPartDescribeLink by a navigate method and from there using the method getDescribedBy to get the WTDocument that the WTPart is described by. Then, non-trivial semantic steps can be made.
After collecting, the objects can be filtered out by a set of defined filters. The filter definition is stored in the same object set registry. The following is an example of a date/time filter:
<Windchill>\codebase\registry\ixb\object_set_handlers\ filter_by_time.xml
<setFilter>
<id>filterByTime</id>
<handler>wt.ixb.objectset.handlers.FilterByTime</handler>
<dialogClassName>
wt.clients.ixb.exp.FilterByTimeDialog
</dialogClassName>
<localizedName>
<localizedString>
<class>wt.ixb.objectset.objectSetResource</class>
<key>FILTER_BY_TIME_NAME</key>
</localizedString>
</localizedName>
<parameters>
</parameters>
</setFilter>
The filtering mechanism, as well as the object collection are coupled with the export application at the level of StandardIXBService. They can be separated.
Adding New Navigators
To implement new Navigator, in most cases you will have to do the following steps:
1. Implement the XML rule file describing the logic of the navigation. See <Windchill>\codebase\registry\ixb\object_set_handlers\product_struct.xml for an example.
2. Implement a subclass of wt.ixb.objectset.handlers.navigator.GeneralNavigator. You may need to implement the following methods:
◦ public boolean canBeUsedAsTopLevelObject(Persistable obj)
◦ public String getTopLevelObjectLocalizedImage(Persistable obj)
|
You must implement all methods that are specified in the rule files.
|
Such navigation methods take the object to navigate from as a parameter, and return Enumeration, which will be added to the export set. For example, if you specify the following rule:
<byMethod>
<method>navigateFromObject1ToObject2</method>
</byMethod>
Then, you must implement the following Java method:
public Enumeration navigateFromObject1ToObject2 (Persistable ob)
throws WTException
List of Existing Generators and Filters
This appendix provides help for the GUI developer who will be using the IX Object Collection mechanism by calling: ObjectSetHelper.computeObjectSetForGivenGeneratorsAndFilters(generatorIds, generatorParams, filterIds, filterParams);
Examples
Part With all Children
genId[0] = “productStructureNavigator”;
genParams[ 0] = “wt.part.WTPart:6789”;
WTHashSet objects = (WTHashSet) ObjectSetHelper.
computeObjectSetForGivenGeneratorsAndFilters( genIds,
genParams,
new String [0 ],
new String [0 ]);
All Objects in the Cabinet and Folder (Including Subfolders)
genId[0] = genId[ 1] = “folderContent”;
genParams[ 0] = “wt.folder.Cabinet:1865”;
genParams[ 1] = “wt.folder.Subfolder:5674”;
WTHashSet objects = (WTHashSet) ObjectSetHelper.
computeObjectSetForGivenGeneratorsAndFilters( genIds,
genParams,
new String [0 ],
new String [0 ]);
| Warning, if there are no filters, you can pass new String[0] for filterIds and filterParams (Don’t pass null, an exception is thrown) |
To get String constants for GUI, see <Windchill>Windchill\srclib\wnc\Carambola-java.jar.
Generators list
String id | Description | Localized name – En (for GUI) | Parameters as String |
---|
folderContent | Collects all objects in a given Cabinet/Folder (including subfolders) | Cabinet and Folder Contents | <class name: oid>, like: wt.folder.Subfolder: 1234 or wt.folder.Cabinet: 1234 |
productStructureNavigator | Collects all children of a given Part (e.g. Parts, which it uses and Documents which describe it) | Product Structure built with active configuration specification | <class name: oid>, like: wt.part.WTPart:123 4 for the top-level object. This object must be instance of WTPart |
productStructureNavigatorEPM | Collects all children of a given CAD Document | CAD Document / Dynamic Document Structure (built with latest configuration specification) | <class name: oid>, like: wt.epm.EPMDocu ment:1234 for the top-level object. This object must be instance of EPMDocument |
productStructureNavigatorWithEPM | Collects all children of a given Part including related CAD Documents | Product Structure with CAD Documents / Dynamic Documents (built with active configuration specification) | <class name: oid>, like: wt.part.WTPart:123 4 for the top-level object. This object must be instance of WTPart |
singleDocument | Takes only given document | Document | <class name: oid>, like: wt.doc.WTDocume nt:1234 |
| Actually <class_name:oid> is object local id There are other generators available which are installation-specific. |
Filters list
String id | Description | Localized name - En (for GUI) | Parameters as String |
---|
filterByTime | Filters out objects with modification time before/after the given interval | Filter based on modification time | <timeFrom#timeTo >, where timeFrom and timeTo = “null” or Timestamp.toString () |
Examples about Exp/Imp Application:
The current Windchill Export OOTB GUI and the StandardIXBService is an Export Application.
The OOTB Windchill Export GUI is Export Application (client) that calls export process in StandardIXBService (Export Application (server)) via IXBHelper.
ObjectExportParameters exportParam = new ObjectExportParameters ();
exportParam.setActionName(actorName);
exportParam.setClientFileName(jarFileName);
exportParam.setContainer(container);
exportParam.setDetailedLog(detailedLog);
exportParam.setGuiId(reqGUIid);
exportParam.setPolicyFile(policyFile);
exportParam.setPreviewOnly(isPreview);
exportParam.setRuleFile(ruleF);
exportParam.setValidation(false);
exportParam.setGenerators(generators);
exportParam.setFilters(filters);
exportParam.setLocale(RB.getLocale());
exportParam.setJarInJarFlag(jarInJar);
exportParam.getContextData().setIXApplicationContext(appContext);
try{
String formatType = WTProperties.getAppletProperties().
getProperty("wt.ixb.export.formatType");
if(formatType != null){
exportParam.setFormatType(ExportImportFormatType.
toExportImportFormatType(formatType));
}
}catch(Exception e){
}
IXBExpImpStatus status = IXBHelper.service.doExport ( exportParam);
• IXBHelper is a class in wt.ixb.clientAccess. It calls methods doExport(…) of the class StandardIXBService to do export process.
• IXBExpImpStatus is a class in wt.ixb.clientsAccess containing information about the Exp/Imp process and is used to pass Exp/Imp status between the server and client.
• generatorIds – see definition above.
• generatorParams is an array of Object Ids of top-level objects that will be exported. From the current Exp GUI, those objects will be chosen by using NavigatorSearchDialog. After the selection is done, this dialog will return a list of IXBSelectedNavInfo with Navigator Id and Generator Id, and seed object as an objectId. Given an object obj, we can get the Object Id by using IXBHelper.service.getObjectId(obj).
• filterIds - see definition above
• filterParams is an array of objects attributes to set the objects to be excluded from export process, or to be included in export process, depends on the type of filters.
• ruleFile is the rule file for export process. This file is provided to Exporter to create a tuner for export process.
• guiId is the id of the GUI from which the export process is called. See the method recordGuiIdInContext() of the class wt.clients.ixb.util.ExpImpServerRequest for an example how to create the GUIid.
• detailLog indicates whether the status message should be in details or not.
• stDtd specifies which version of the Exp/Imp handlers will be used. This is used to support backward compatible. If stDtd is null or empty (“”), the STRING_DTD will be calculated based on the current Windchill.
• When the method IXBHelper.service.doExport(…) is called, it will call to the method doExportImpl(…) in the StandardIXBService.
This method:
◦ Creates a general export handler ExportHandler handler. This is an inner class of StandardIXBService.
◦ Gets a list of objects that will be exported by calling
ObjectSetHelper.computeObjectSetForGivenGeneratorsAndFilters (
generatorIds,
generatorParams,
filterIds,
filterParams);
◦ Creates an instance of Exporter, the class that does the export.
◦ Depending on isPreview (true/false) the exporter will do a preview or real export by calling methods of Exporter class mention in the section Exporter class of this document.
◦ Calls clean-up methods of the ExportHandler handler.
Import Application
The current Windchill Import GUI and StandardIXBService are the Import Application. The current Windchill Import OOTB GUI is Import Application client that calls import process in StandardIXBService (Import Application server) via IXBHelper.
ObjectImportParameters importParam = new ObjectImportParameters ();
importParam.setActionName(actorName);
importParam.setContainer(container);
importParam.setDataFile(dataF);
importParam.setDetailedLog(detailedLog);
importParam.setGuiId(reqGUIid);
importParam.setPolicyFile(policyFile);
importParam.setPreviewOnly(isPreview);
importParam.setRuleFile(ruleF);
importParam.setValidation(true);
importParam.setLocale(RB.getLocale());
importParam.setOverrideConflicts(overrideC);
importParam.setContainerMappingFile(containerMappingFile);
importParam.getContextData().setIXApplicationContext(appContext);
IXBExpImpStatus status = IXBHelper.service.doImport (importParam );
It calls methods doImport(…) of the class StandardIXBService to do the import process.
IXBExpImpStatus is a class in wt.ixb.clientsAccess containing information about Exp/Imp process and used to pass Exp/Imp status between server and client.
ruleFile is the rule file for export process. This file is provided to Importer to create a tuner for import process.
dataFile is the jar file that contains XML files of objects that will be imported.
overrideConflicts specifies whether overridable conflicts must be overridden or not.
isPreview specifies whether the process should do real import, or check conflicts and display what objects will be imported.
guiId is the id of the GUI from which the export process is called. See the method recordGuiIdInContext() of the class wt.clients.ixb.util.ExpImpServerRequest for an example how to create the GUIid.
detailLog indicates whether the status message should be in details or not.
creatorName specifies how top-level imported objects (for example EPMDocument, WTDocument, WTPart) are created.
stDtd specifies which version of Exp/Imp handlers will be used. This is used to support backward compatible. If stDtd is null or empty (“”), the STRING_DTD will be calculated based on version of current Windchill system.
When the method IXBHelper.service.doImport(…) is called, it will call to the method doImportImpl(…) in StandardIXBService.
This method:
• Puts creator name in WTContext to be used by import handler.
• Creates a general import handler ImportHandler handler.
• Gets a list of XML files from the Jar file to be imported by calling jar.getFileNamesByExtension ("xml");
• Creates an instance of Importer, the class that does the import job.
• Depending on isPreview (true/false), the method doImportImpl(…) calls the appropriate methods of Importer to do a preview or the real import:
◦ importer.doImport(stream);
◦ importer.doPreview(stream);
• The others (importer.doImport(fn, tag) and importer.doPreview(fn,tag)) are for optimization, and they depend on how XML files are named. This feature is just for a particular Exp/Imp Application (wt.clients.ixb and StandardIXBService).
• Sends log messages back to client.
Simple Export Handler Code Sample
import java.io.File;
import java.io.PrintStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;
import wt.pom.Transaction;
import wt.content.ApplicationData;
import wt.content.ContentItem;
import wt.content.Streamed;
import wt.ixb.publicforapps.ApplicationExportHandlerTemplate;
import wt.ixb.publicforhandlers.IxbElement;
import wt.ixb.publicforapps.Exporter;
import wt.ixb.publicforapps.IxbHelper;
import wt.ixb.objectset.ObjectSetHelper;
import wt.util.WTException;
import wt.util.WTMessage;
import wt.ixb.clientAccess.IXBJarWriter;
import wt.fc.Persistable;
public class SimpleApplicationExportHandler extends ApplicationExportHand
lerTemplate{
private File targetDir = null;
private PrintStream log = null;
private IXBJarWriter jw = null;
private File resJar = null;
private int fileNum = 0; //counter for exported content files
private HashSet contentFileNames = new HashSet(); //to handle
content files with the same name
public static final String NAME_IS_TAG = "TAG";
public final static String CONTENT_SUBDIR = "CONTENTS";
public SimpleApplicationExportHandler(File tmp_storeDir, PrintStream a_log)
throws WTException{
if (!tmp_storeDir.exists()){
tmp_storeDir.mkdirs();
}
targetDir = tmp_storeDir;
log = a_log;
}
public String storeContent (Object ob) throws WTException
{
String storeName = null;
if(ob instanceof ApplicationData)
{
ApplicationData obj = (ApplicationData)ob;
String fileName = obj.getFileName();
try
{
storeName = this.computeUniqueFileName(fileName);
Streamed sd = (Streamed)obj.getStreamData().getObject();
InputStream is = sd.retrieveStream();
jw.addEntry(is, storeName);
}
catch (IOException ioe)
{
throw new WTException(ioe);
}
}
return storeName;
}
public String storeDocument(IxbElement elem, String dtd)
throws WTException
{
try {
String tag = elem.getTag();
String fn = NAME_IS_TAG+"-"+tag+"-"+(fileNum++)+".xml";
File file = new File(targetDir,fn);
FileOutputStream stream
= new FileOutputStream (file);
elem.store(stream, dtd); stream.close();
jw.addEntry(file);
file.delete();
}
catch (IOException ioe){
throw new WTException(ioe);
}
}
public void storeLogMessage(String resourceBundle,
String messageKey,
Object[] textInserts)
throws WTException{
WTMessage m = new WTMessage(resourceBundle, messageKey, textInserts);
String s = m.getLocalizedMessage();
log.println(s);
}
public void storeLogMessage(String resourceBundle, String messageKey,
Object[] textInserts, int importanceLevel)
throws WTException{
storeLogMessage (resourceBundle, messageKey, textInserts);
}
public void exportObjectContent (Object obj, Exporter exporter, Content
Item item, String exportFileName)
throws WTException {
if (item instanceof ApplicationData) {
ApplicationData ad = (ApplicationData) item;
Streamed streamedIntfc = (Streamed) ad.getStreamData().getObject();
try{
InputStream is = streamedIntfc.retrieveStream();
jw.addEntry(is, exportFileName);
}
catch (IOException ioe){
throw new WTException(ioe);
}
}
}
private String computeUniqueFileName (String fn) throws IOException {
//compute file name in jar (should be unique)
if (contentFileNames.contains(fn)) {
// if simple content's name already has been used then look for
// name in form name-123.ext
// As a result will have names like: design.doc, design-23.doc,
design-57.doc, ...
int i = fn.lastIndexOf('.');
String fn_n = ( i>0 ? fn.substring(0, i) : fn);
String fn_t = ( i>0 ? fn.substring(i+1) : "" );
while (true) {
fn = (i>0 ?
fn_n + '-' + (fileNum++) + '.' + fn_t :
fn_n + '-' + (fileNum++)
);
if (!contentFileNames.contains(fn)) break;
}
}
contentFileNames.add(fn);
String fnInJar = CONTENT_SUBDIR + "/" + fn;
return fnInJar;
}
public File doExport( WTContainerRef container,
String[] generatorIds,
String[] generatorParams,
String[] filterIds,
String[] filterParams,
File ruleFile,
File policyFile,
String actionName,
String stDtd,
File resultingJar)
throws WTException{
//init jar file
resJar = resultingJar;
try{
jw = new IXBJarWriter(resultingJar);
}
catch (IOException ioe){
throw new WTException(ioe);
}
//adopt incoming rule file
IxbElement clientSettingsElement = null;
if (ruleFile!=null) {
try{
InputStream clientSettingsStream = new FileInputStream(ruleFile);
clientSettingsElement = IxbHelper.newIxbDocument
(clientSettingsStream,
false);
}
catch(IOException ioe){
throw new WTException(ioe);
}
}
//create exporter
Exporter exporter = null;
if ( policyFile==null ) { // policy rule is null; export action is
expected ...
exporter = IxbHelper.newExporter (this, IxbHelper.STANDARD_DTD,
clientSettingsElement, null, actionName );
}
else{
exporter = IxbHelper.newExporter (this, IxbHelper.STANDARD_DTD,
clientSettingsElement, policyFile, null );
}
//gen set of items to export
Set res = ObjectSetHelper.computeObjectSetForGivenGeneratorsAndFil
ters (generatorIds, generatorParams, filterIds, filterParams);
Iterator iter = res.iterator();
Transaction trx = new Transaction();
try {
if ( !(actionName != null && actionName.equals(wt.ixb.tuner.Exp
ortActionHelper.NO_ACTION_CMD) )){
trx.start();
}
while (iter.hasNext()) {
Persistable ob = (Persistable)iter.next();
exporter.doExport(ob);
}
exporter.finalizeExport();
if ( !(actionName != null && actionName.equals(wt.ixb.tuner.Exp
ortActionHelper.NO_ACTION_CMD) )){
trx.commit();
}
trx = null;
}
finally {
if (trx != null) {
if ( !(actionName != null && actionName.equals(wt.ixb.tuner.
ExportActionHelper.NO_ACTION_CMD) )){
trx.rollback();
}
trx = null;
}
}
try{
jw.finalizeJar();
}
catch(IOException ioe){
throw new WTException (ioe);
}
return resJar;
}
}
Simple Import Handler Code Sample
import java.io.File;
import java.io.PrintStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;
import wt.pom.Transaction;
import wt.content.ApplicationData;
import wt.content.ContentItem;
import wt.content.Streamed;
import wt.ixb.publicforapps.ApplicationImportHandlerTemplate;
import wt.ixb.publicforhandlers.IxbElement;
import wt.ixb.publicforhandlers.IxbHndHelper;
import wt.ixb.publicforapps.IxbDocument;
import wt.ixb.publicforapps.Importer;
import wt.ixb.publicforapps.IxbHelper;
import wt.ixb.objectset.ObjectSetHelper;
import wt.ixb.actor.actions.IxbActionsHelper;
import wt.util.WTException;
import wt.util.WTMessage;
import wt.ixb.clientAccess.IXBJarReader;
import wt.fc.Persistable;
import javax.xml.transform.stream.StreamSource;
public class SimpleApplicationImportHandler
extends ApplicationImportHandlerTemplate{
private IXBJarReader jr = null;
private PrintStream log = null;
public SimpleApplicationImportHandler(PrintStream a_log){
log = a_log;
}
public InputStream getContentAsInputStream (String contentId)
throws WTException {
try{
return jr.getStreamByName (contentId);
}
catch(IOException ioe){
throw new WTException(ioe);
}
}
public void storeLogMessage(String resourceBundle,
String messageKey, Object[] textInserts)
throws WTException{
WTMessage m = new WTMessage(resourceBundle, messageKey, textInserts);
log.println(m.getLocalizedMessage());
}
public void doImport(WTContainerRef container,
File ruleFile,
File dataFile,
boolean _overrideConflicts,
String actorName,
File policyFile,
File containerMappingFile)
throws WTException{
try{
jr = new IXBJarReader(dataFile);
}
catch(IOException ioe){
throw new WTException (ioe);
}
//prepare rule file
String ruleFileName = (ruleFile!=null)? ruleFile.getAbsolutePath(): null;
//prepare container mapping file
String containerMappingFileName = (containerMappingFile!=null)?containerMappingFile.getAbsolutePath(): null;
//prepare policy file
String policyFileName = (policyFile!=null)?policyFile.getAbsolutePath(): null;
StreamSource xslPolicyFile = null;
if (policyFile!=null) {
xslPolicyFile = new StreamSource(policyFile.getAbsolutePath());
}
Boolean overrideConflicts = new Boolean (_overrideConflicts);
Importer importer = IxbHelper.newImporter(this,
container,
IxbHelper.STANDARD_DTD,
ruleFileName,
policyFileName,
containerMappingFileName,
actorName,
overrideConflicts,
null /*validate*/);
String [] fns = jr.getNonContentFileNamesByExtension ("xml");
boolean validate = IxbHndHelper.getIxbProperty("import.parser.validate",
false);
for (int i=0; i<fns.length; i++) {
String fn = fns[i];
InputStream stream = null;
try{
stream = jr.getStreamByName (fns[i]);
}
catch (IOException ioe){
throw new WTException (ioe);
}
IxbDocument doc = IxbHelper.newIxbDocument(stream, validate);
//if policyFile == null, apply actorName to XML Document before pass
it to importer
if (policyFile == null){
IxbElement rootElem = doc.getRootElement();
//XML_ACTION_KEY = "actionInfo/action"
IxbElement actionElement =
doc.getElement(IxbActionsHelper.XML_ACTION_KEY);
if (actionElement == null){
rootElem.addValue(IxbActionsHelper.XML_ACTION_KEY,
actorName);
}
else {
rootElem.removeChildElement( actionElement);
rootElem.addValue(IxbActionsHelper.XML_ACTION_KEY,
actorName);
}
}
else { //apply policy file
doc =
IxbActionsHelper.writeActionAndParametersToFileXML(doc,xslPolicyFile);
}
//set elem ready for import
importer.doImport(doc);
}
//perform actual import
importer.finalizeImport();
}
}