Skip Headers

Agile Product Lifecycle Management SDK Developer Guide - Using APIs Release 9.3.3 E39307-01 |
| Contents

Previous | Next |

# 13 Working with Attachments and File Folder Objects

Attachments to objects contain information about the object or a manufacturing process. You can attach files and URLs by referencing them in a File Folder object. The File Folder object holds pertinent content, or Attachments. Most primary Agile API objects, such as IItem, IChange, IManufacturer, IManufacturerPart, IPackage, ITransferOrder, IUser, and IUserGroup, have an Attachments table (or tab in the Java Client) that lists indirect references to the files or URLs that are in separate file folders. Each row in an Attachments table can refer to one file or to all files from a referenced file folder.

The following illustration is an example of the way files or URLs contained in a file folder are referenced indirectly from the Attachments table of multiple business objects, in this case an item and a change.

Figure 13-1 Referenced item and a change

The Agile API does not provide support for viewing or printing an attachment. However, after you download a file, you can use another application to view, edit, or print the attachment.

A File Folder is a business object that specifies one or more files or URLs that are stored in the file server vault. In addition, a file folder has its own set of tables. This means that you can create and load an independent file folder and add one or more files to its Files table. You can also search for a file folder, just as you would search for an Item or Change.

Important Before you try to add Agile PLM attachments and work with file folders, make sure the File Manager Internal Locator property is set in Agile Java Client. Choose Admin > Settings > Server Settings > Locations > File Manager > Advanced > File Manager Internal Locator. The format for the value is <protocol>://<machinename>:<port>/<virtualPath>/services/FileServer. For example, `http://agileserver.agile.agilesoft.com:8080/Filemgr/services/FileServer` is a valid value. For more information about Agile PLM server settings, refer to the Agile PLM Administrator Guide.

## 13.1 Working with File Folders

Similar to Attachments, the SDK exposes APIs to perform File Folders-related tasks such as checking-in and checking-out files associated with objects in the rows of an Attachments table, adding files and URLs to an Attachments table, and deleting attachments. This section lists and describes these features, and provides the necessary procedures to use the SDK to perform these tasks.

### 13.1.1 File Folder Classes and Subclasses

The File Folder Base Class has two Classes and each of these classes have their own respective Subclasses. The figure below lists the File Folders Base Class, Classes, and Subclasses. The Agile PLM administrator can define new file folder subclasses.

Figure 13-2 File Folders Classes and Subclasses

A description of these classes and objects appears in the table below.

Base Class | Class | Subclass | Description
--- | --- | --- | ---
File Folders | Designs | Design | Objects that permit building model structures in CAD
File folders | File Folder Markup | Objects that include files or URLs; this class includes all file folder objects except historical report file folders.

For information about routing these objects, see Checking the State of Agile PLM Objects on page 33.

### 13.1.2 File Folder Tables and Constants

The File Folder object supports the following tables and corresponding constants:

Table | Constant | Read/Write Mode
--- | --- | ---
Title Block | TABLE_TITLEBLOCK | Read/Write
Page Two | TABLE_PAGETWO | Read/Write
Page Three | TABLE_PAGETHREE | Read/Write
Files | TABLE_FILES | Read/Write
Structure | TABLE_STRUCTURE | Read/Write
Routing Slip/Workflow | TABLE_WORKFLOW | Read/Write
Relationships | TABLE_RELATIONSHIPS | Read-only
History | TABLE_HISTORY | Read-only
Where Used | TABLE_WHEREUSED | Read/Write
Where Used Design | TABLE_WHEREUSEDDESIGN | Read-only

### 13.1.3 Creating File Folder Objects

IFileFolder is the interface that corresponds to the file folder business object. The following example shows how to create a file folder.

Example: Creating a file folder

    public void createFileFolder() throws Exception {
    IAgileClass attClass =

m_admin.getAgileClass(FileFolderConstants.CLASS_FILE_FOLDER);

    IAutoNumber an = cls.getAutoNumberSources()[0];
    String attNumber = an.getNextNumber();

IFileFolder ff = (

IFileFolder)m_session.createObject(attClass, attNumber);

ff.checkOutEx();

    }
    FileFolderConstants.CLASS_DESIGN, autoNum);

Note When you add a file or a URL to the row of the Attachments table of a business object, you will automatically create automatically a new file folder object that contains the associated file or URL. See Creating File Folder Objects by Adding Rows to Attachments Table on page 200.

The File Folders Design class is similar to the File folder class with the additional Structures table (Tab in the Java Client UI) for CAD objects. The following examples show how to create a Design object, adding a Design object to a the Structure tree, and loading a structure table.

Example: Creating a Design object

// autoNum is autoNumber as usual

    IFileFolder obj = (IFileFolder) m_session.createObject(

Example: Adding Design objects to a Structure tree

    IFileFolder obj = // some Design object
    IFileFolder childObj1 = // some Design object
    IFileFolder childObj2 =  // some Design object
    obj.checkOutEx();
    ITable table = obj.getTable(FileFolderConstants.TABLE_STRUCTURE);

// add row

    Object[] vers = childObj1.getVersions();
    IRow row = table.createRow(childObj1);
    row.setValue(FileFolderConstants.ATT_STRUCTURE_LABEL,

"label modified by creating row 1");

    row = table.createRow(childObj2);
    row.setValue(FileFolderConstants.ATT_STRUCTURE_LABEL,

"label modified by creating row 2");

    obj.checkIn();
    Integer tableId = (Integer) table.getTableDescriptor().getId();
         public void testLoadingDesignStructureTable() throws Exception {
               addCaseInfo("Design Object", "load Structure table", "");

Example: Loading a Structure table

// assuming Design object Design00004 existed with some data in Structure

    IFileFolder obj = (IFileFolder) m_session.getObject(

FileFolderConstants.CLASS_DESIGN, "Design00004");

IAgileClass agileClass = obj.getAgileClass();

// load Structure table

    ITable table = obj.getTable(FileFolderConstants.TABLE_STRUCTURE);
    // ITable performs related tasks

}

Example: Loading a Structure table as a tree

    public void testLoadingDesignStructureTree()
    throws Exception

{addCaseInfo("Design Object", "load Structure tree", "");

// assuming Design object Design00004 existed with some data in Structure

    IFileFolder obj = (IFileFolder) m_session.getObject(
    if(row instanceof IRow) {

FileFolderConstants.CLASS_DESIGN, "Design00004");

IAgileClass agileClass = obj.getAgileClass();

// load Structure table

ITable table = obj.getTable(FileFolderConstants.TABLE_STRUCTURE);

Integer tableId = (Integer) table.getTableDescriptor().getId();

ITreeNode root = (ITreeNode) table;

Collection topLevelChildren = root.getChildNodes();

Iterator it;

ITreeNode row;

if (topLevelChildren != null) {

it = topLevelChildren.iterator();

int level = 0;

while (it.hasNext()) {

    row = (ITreeNode) it.next();
                   IRow aRow = (IRow) row;
                   IDataObject referent =
                        aRow.getReferent();
                   if(referent != null) {
                      System.out.println(
                       "Row Referent Object ID/row:
                       "+ referent.getObjectId()+ " / "
                       + referent.getName());
                                 }
                           }
                 iterateTreeNode(agileClass, true,
                        tableId, (ITreeNode) row);
                           count++;
                     }
               }

System.out.println("The number of rows in top level is " + count);

    }
    ("Row Referent Object ID/row:
    private void iterateTreeNode(IAgileClass agileClass, boolean print, Integer tableId, ITreeNode node) throws APIException {

Collection childNodes = node.getChildNodes();

printRow(agileClass, print, tableId, (IRow) node);

if (childNodes == null || childNodes.size() <= 0) {

return;

    }

Iterator it = childNodes.iterator();

ITreeNode childNode;

IRow row;

while (it.hasNext()) {

childNode = (ITreeNode) it.next();

if (childNode instanceof IRow) {

row = (IRow) childNode;

if(row instanceof IRow) {

IDataObject referent =

row.getReferent();

if(referent != null) {

    System.out.println
                  "+ referent.getObjectId()+ " / "
                  + referent.getName());
                            }
                      }
    iterateTreeNode(agileClass, print, tableId, (ITreeNode) childNode);
                }
          }
    }

### 13.1.4 Creating File Folder Objects by Adding Rows to Attachments Table

When you add a file or a URL to the row of the Attachments table of a business object, you automatically create a new file folder that contains the associated file or URL. You can load the referenced file folder using the IRow.getReferent() method, as shown in the following example.

Example: Creating a file folder by adding a row to the Attachments table

    public IFileFolder addRowToItemAttachments
    (IItem item, File file) throws Exception
      {ITable attTable = item.getTable(ItemConstants.TABLE_ATTACHMENTS);
      IRow row = attTable.createRow(file);
      IFileFolder ff = (IFileFolder)row.getReferent();
      return ff;
    }

### 13.1.5 Working with the Files Table of a File Folder

The Files table of a file folder lists the files and URLs associated with the object. To edit the table, you must first check out the file folder. You cannot add files or URLs to the Files table or delete them unless the file folder is checked out.

The following example shows how to check out a file folder and then add files and URLs to the Files table.

Example: Adding files and URLs to the Files table of a file folder

    public void addFiles(IFileFolder ff, File[] files, URL[] urls) throws Exception {
    // Check out the file folder

ff.checkOutEx();

    // Get the Files table
    // Get Item P1000
    // Add files to the Files table

ITable filesTable = ff.getTable(FileFolderConstants.TABLE_FILES);

for (int i = 0; i < files.length; ++i) {

filesTable.createRow(files[i]);

}

    // Add URLs to the Files table

for (int i = 0; i < urls.length; ++i) {

filesTable.createRow(urls[i]);

}

    // Check in the file folder
    }

ff.checkIn();

### 13.1.6 Accessing Files in Agile PLM File Vault with IAttachmentFile

IAttachmentFile is the interface that provides generalized access to files stored in the Agile PLM file vault. This interface is supported by the following Agile API objects:

- File folder - you can class cast IFileFolder to IAttachmentFile.

- A row of the Files table of a file folder - you can class cast IRow from the Files table to IAttachmentFile.

- A row of the Attachments table of a business object - you can class cast IRow from the Attachments table to IAttachmentFile.

IAttachmentFile provides the following methods for working with attachments:

- getFile()

- isSecure()

Note: IAttachmentFile also has a setFile() method that lets you change the file(s) for an attachment, but it is supported only for rows of the Attachments table.

The results returned from IAttachmentFile methods vary depending on the object you're working with, as shown in the following table.

Calling object | getFile() return value | isSecure() return value
--- | --- | ---
Row from the Attachments table of any business object | Returns either a single file InputStream if the row refers to a specific file from the file folder or a zipped InputStream with all the files from the file folder. | true if the referenced file is not URL, or all the files are not URLs.
FileFolder object | Returns a zipped InputStream with all files from the file folder. | true if all the files contained in the file folder are not URLs.
Row from the Files table of a file folder | Returns a single file InputStream that refers to a specific file from the file folder. | true if the referenced file is not a URL.

Note: To read files in a zipped InputStream , use methods of the java.util.zip.ZipInputStream class.

The following example shows how to use IAttachmentFile.isSecure() and IAttachmentFile.getFile() from the row of an Attachments table for an item.

Example: Using isSecure() and getFile()

    public InputStream getItemAttachment(IItem item) throws Exception {

InputStream content = null;

ITable attachments = item.getTable(ItemConstants.TABLE_ATTACHMENTS);

IRow row = (IRow)attachments.iterator().next();

if (((IAttachmentFile)row).isSecure())

content = ((IAttachmentFile)row).getFile();

return content;

    }

## 13.2 Working with Attachments Table of an Object

To work with the Attachments table of an object, follow this sequence.

1. Get the object that has the attachment you want.

   For example, you can use the IAgileSession.getObject() method to get a particular object, or you can create a query to return objects.

2. Get the Attachments table. Use the IDataObject.getTable() or IAttachmentContainer.getAttachments() methods to get the table.

3. Select a row in the Attachments table.

Create an iterator for the table, and then select a particular row. You can use the ITable.getTableIterator() method to get a bidirectional iterator for the table.

The following example below shows how to retrieve an item, get the Attachments table for the item, and then select the first attachment.

Example: Getting an attachment for an Item

    try {

Map params = new HashMap();

params.put(ItemConstants.ATT_TITLE_BLOCK_NUMBER, "P1000");

IItem item =

(IItem)m_session.getObject(IItem.OBJECT_TYPE, params);

    // Get the attachment table for file attachments
    public checkInRow(IRow row, String filePath) throws APIException {
    // Get a table iterator

ITable attTable = item.getAttachments();

ITwoWayIterator it = attTable.getTableIterator();

    // Get the first attachment in the table

if (it.hasNext()) {

IRow row = (IRow)it.next();

    // Read the contents of the stream

InputSteam stream = ((IAttachmentFile)row).getFile();

}

else {

JOptionPane.showMessageDialog(null, "There are no files listed.",

"Error", JOptionPane.ERROR_MESSAGE);

}

    } catch (APIException ex) {
    }

System.out.println(ex);

### 13.2.1 Checking In and Checking Out Files with ICheckoutable

ICheckoutable is an interface that you can use to check in and check out files that are associated with an object. This applies only to rows of the Attachments table. You can class cast IRow from the Attachments table to ICheckoutable.

ICheckoutable provides the following methods for working with attachments:

- cancelCheckout()

- checkIn()

- checkOutEx()

- isCheckedOut()

This example shows how to use the ICheckoutable interface to check out and check in a file from a row of the Attachments table.

Example: Using ICheckoutable methods to check out and check in an attached file

    public InputStream checkOutRow(IRow row) throws APIException {
    // Check out the attachment

((ICheckoutable)row).checkOutEx();

    // Read the contents of the stream

InputStream stream = ((IAttachmentFile)row).getFile();

return stream;

    }

if (row.isCheckedOut()) {

    // Set the new file
    // Show a confirmation dialog box
    // Check in the file

((IAttachmentFile)row).setFile(new File(filePath));

((ICheckoutable)row).checkIn();

}

else {

JOptionPane.showMessageDialog(null, "The attachment is not checked out.",

"Error", JOptionPane.ERROR_MESSAGE);

}

    }

### 13.2.2 Specifying the Revision of the Item

When you are working with items, each revision can have different attachments. If an item has multiple revisions, your program should allow the user to select a revision. For information about specifying the revision, see Getting and Setting the Revision of an Item.

### 13.2.3 Checking whether the Revision Is Incorporated

When the revision for an item is released, it is possible the revision is also incorporated. The attachments for an incorporated item are locked and cannot be checked out.

However, you can still view incorporated attachments, but you cannot modify them. To modify an incorporated attachment, you must either un-incorporate the attachment, or submit a new change order to create a new revision as shown in the examples below. These two examples are Process Extensions. For information on Process Extensions, refer to SDK Developer Guide - Developing PLM Extensions and for information about checking if a revision is incorporated, see Changing the Incorporated Status of a Revision.

Example: Incorporating Attachments

    class IncorporateItem implements ICustomAction {

public ActionResult doAction(IAgileSession session, INode actionNode,

IDataObject affectedObject) {

try {

System.out.println("Workflow action kicked off....");

IItem object = (IItem)affectedObject;

System.out.println("Incorporating...");

IItem loItem = (IItem) session.getObject(IItem.OBJECT_TYPE, object.getName());

    //this will get the latest version. Make sure the latest is against a MCO
    //incorporate the attachment

loItem.setIncorporated(true);

System.out.println("Attachment added.");

String message = ("Incorporated"+object);

return new ActionResult(ActionResult.STRING, message);

} catch (APIException ae) {

ae.printStackTrace();

return new ActionResult(ActionResult.EXCEPTION, ae);

}

}

    }

Example: Un-incorporating Attachments

    class IncorporateItem implements ICustomAction {

public ActionResult doAction(IAgileSession session, INode actionNode,

IDataObject affectedObject) {

try {

System.out.println("Workflow action kicked off....");

IItem object = (IItem)affectedObject;

System.out.println("Un-incorporating...");

IItem loItem = (IItem) session.getObject(IItem.OBJECT_TYPE, object.getName());

    //this will get the latest released version

ssage);

loItem.setIncorporated(false);

System.out.println("Attachment added.");

String message = ("Un-incorporated"+object);

return new ActionResult(ActionResult.STRING, message

} catch (APIException ae) {

ae.printStackTrace();

return new ActionResult(ActionResult.EXCEPTION, ae);

}

}

    }

## 13.3 Checking Out a File Folder

Before you can add, delete, or modify the files contained in a file folder, you must check out the file folder. With the appropriate privileges, you can check out a file folder as long as it is not already checked out by another user. Once a file folder is checked out, no one else can check it out or modify it.

The user who checked out a file folder, as well as other users who are change analysts or component engineers, can check it in. If the file folder was checked out to a location on the network, or to a shared drive or directory, anyone who has access to that network location or to that shared directory can check in the file folder.

The following example shows how to check out a file folder.

Example: Checking out a file folder

    void checkOutFileFolder(IFileFolder ff) throws Exception {
    }

ff.checkOutEx();

Note You can also use ICheckoutable.checkOutEx() to check out a row of the Attachments table. See Checking In and Checking Out Files with ICheckoutable on page 203 .

## 13.4 Canceling a File Folder Checkout

If you check out a file folder and then decide that you don't want to modify it, or you want to discard your changes and revert to the original file folder, you can cancel the checkout. When you cancel a checkout, you also make the file folder available for other users to check out.

Note: Only the user who checked out a file folder can cancel the checkout.

This example shows how to cancel a checkout of a file folder.

Example: Canceling checkout of a file folder

    void cancelCheckOut(IFileFolder ff) {

int i = JOptionPane.showConfirmDialog(null,

"Are you sure you want to cancel checkout?",

"Cancel Checkout", JOptionPane.YES_NO_OPTION);

    // If the user clicks Yes, cancel checkout
    public static IRow addAttachment(ITable attTable, File[] files) throws APIException {

try {

if (i == 0) {

ff.cancelCheckout();

}

} catch (APIException ex) {

System.out.println(ex);

}

    }

Note: You can also use ICheckoutable.cancelCheckout() to cancel checkout of a row of the Attachments table. See Checking In and Checking Out Files with ICheckoutable on page 203.

## 13.5 Adding Files and URLs to the Attachments Table

The Agile API lets you add files and URLs to the Attachments table of many types of objects, such as IItem, IChange, IManufacturerPart, and IManufacturer. An attachment is one or more physical files or an Internet address (URL). A file is considered a secured attachment because it is physically stored in the Agile PLM file vault. A URL, on the other hand, is an unsecured attachment.

When you add a file or a URL to the Attachments table of a business object, the server automatically creates a new file folder containing the associated file or URL. The new row on the Attachments table references the new file folder.

When you add a URL attachment, the server stores a reference to the Internet location but does not upload a file. Therefore, you cannot download a URL attachment. The Agile API validates URL strings that you attempt to check in as an attachment. If a URL is invalid, the Agile API considers the string a filename instead of a URL.

You cannot add a file or URL to the Attachments table of an item if

- The current revision has a pending or released MCO.

- The current revision is incorporated.

When you use the ITable.createRow(java.lang.Object) method to add a row to the Attachments table, the param method can be any of the following object types:

- String - adds one file attachment specified by a local path.

- String[] - adds multiple file attachments specified by an array of local paths.

- File - adds one file attachment.

- File[] - adds multiple file attachments.

- InputStream - adds one file attachment.

- InputStream[] - adds multiple file attachments.

- URL - adds one URL attachment.

- URL[] - adds multiple URL attachments.

- IRow (of the Attachments or Files tables) - adds a file or URL attachment.

- IFileFolder - adds all files and URLs for the specified file folder.

- Map - adds one or more files specified by a hash table containing Attachment parameters.

Note: The File object type performs best when adding attachments.

When you add a file or a URL to the row of the Attachments table of a business object, you automatically create a new file folder that contains the associated file or URL. You can load the referenced file folder using the IRow.getReferent() method, as shown in the following example.

Example: Creating a file folder by adding a row to the Attachments table

    public IFileFolder addRowToItemAttachments

(IItem item, File file) throws Exception

{ITable attTable = item.getTable(ItemConstants.TABLE_ATTACHMENTS);

IRow row = attTable.createRow(file);

IFileFolder ff = (IFileFolder)row.getReferent();

return ff;

    }

This example uses several instances of the addAttachment() methods to illustrate the different ways you can add rows to an Attachments table.

Example: Adding files to the Attachments table

    // Add a single file to the Attachments table row by specifying a file path

public static IRow addAttachment(ITable attTable, String path) throws APIException {

IRow row = attTable.createRow(path);

return row;

}

    // Add a single file to the Attachments table

public static IRow addAttachment(ITable attTable, File file) throws APIException {

IRow row = attTable.createRow(file);

return row;

}

    // Add multiple files to the Attachments table

IRow row = attTable.createRow(files);

return row;

    }
    // Add a file folder to the Attachments table and specify the version for all files
    // Add a URL attachment to the Attachments table
    public static IRow addAttachment(ITable attTable, URL url) throws APIException {

IRow row = attTable.createRow(url);

return row;

    }
    // Add a file folder to the Attachments table
    public static IRow addAttachment(ITable attTable, IFileFolder ff) throws APIException {

IRow row = attTable.createRow(ff);

return row;

    }
    // Add a FileFolder.Files row object or a [BusinessObject].Attachments row object
    // to the Attachments table. The Agile API validates the row object at run time to
    // determine if it is from a valid table (Files or Attachments).
    public static IRow addAttachment(ITable attTable, IRow filesRow) throws APIException {

IRow row = attTable.createRow(filesRow);

return row;

    }

public static IRow addAttachmentWithVersion(ITable attTable, IFileFolder ff) throws APIException {

ff.setCurrentVersion(new Integer(1));

IRow row = attTable.createRow(ff);

return row;

}

### 13.5.1 Deep Cloning Attachments and Files from One Object to Another

To simplify copying file attachments from one object to another, use the CommonConstants.MAKE_DEEP_COPY virtual attribute as a Boolean parameter of ITable.createRow(Object). This parameter allows your program to create a new copy of the file in the Agile File Manager vault instead of referencing the old file.

Example: Deep cloning an Attachments table row

    // Clone an attachment table row and its file from one item to another
    public static cloneAttachment(IItem item1, IItem item2, File file) throws APIException {
    // Get the attachments tables of item1 and item2

ITable tblAttach1 = item1.getAttachments();

ITable tblAttach2 = item2.getAttachments();

    // Prepare params for the first row
    public static cloneFilesRow(IFileFolder folder1, IFileFolder folder2, File file) throws APIException {

HashMap params = new HashMap();

params.put(CommonConstants.ATT_ATTACHMENTS_CONTENT, file);

    // Add the file to the attachments table of item1
    // Prepare params for the second row

IRow row1 = tblAttach1.createRow(params);

params.clear();

params.put(CommonConstants.ATT_ATTACHMENTS_CONTENT, row1);

params.put(CommonConstants.MAKE_DEEP_COPY, Boolean.TRUE);

    // Add the same file to the attachments table of item2
    }

IRow row2 = tblAttach2.createRow(params);

Example: Deep cloning the Files table row of a File Folder

    // Clone a Files table row and its file from one File Folder to another
    // Check out folder1 and folder2

folder1.checkOutEx();

folder2.checkOutEx();

// Get the Files tables of folder1 and folder2

ITable tblFiles1 = folder1.getTable(FileFolderConstants.TABLE_FILES);

ITable tblFiles2 = folder2.getTable(FileFolderConstants.TABLE_FILES);

    // Prepare params for the first row
    // init item

HashMap params = new HashMap();

params.put(CommonConstants.ATT_ATTACHMENTS_CONTENT, file);

    // Add the file to the attachments table of folder1
    // Prepare params for the second row

IRow row1 = tblFiles1.createRow(params);

params.clear();

params.put(CommonConstants.ATT_ATTACHMENTS_CONTENT, row1);

params.put(CommonConstants.MAKE_DEEP_COPY, Boolean.TRUE);

    // Add the same file to the Files table of folder2
    // Check in folder1 and folder2

IRow row2 = tblFiles2.createRow(params);

folder1.checkIn();

folder2.checkIn();

    }

### 13.5.2 Specifying the File Folder Subclass When Adding Attachments

You can set up your Agile PLM system with multiple file folder subclasses. If so, when you add a file folder to the Attachments table of a business object, you may want to specify which file folder subclass to use. If you do not specify a subclass, the Agile API uses the default File Folder subclass. The virtual attribute CommonConstants.ATT_ATTACHMENTS_FOLDERCLASS makes it easier to specify the required file folder subclass. It enables you to set the attribute to any file folder subclass.

The following example shows how to use the ATT_ATTACHMENTS_FOLDERCLASS attribute to specify a subclass when you add a file folder to the Attachments table.

Example: Specifying the file folder subclass when adding attachments

    IAgileClass ffclass = m_admin.getAgileClass("MyFileFolder");
    IItem item = (IItem)session.createObject(ItemConstants.CLASS_PART, "P0001");
    // get attachments table
    ITable tab_attachment = item.getAttachments();

    // prepare map
    HashMap map = new HashMap();
    map.put(CommonConstants.ATT_ATTACHMENTS_CONTENT, new File("files/file.txt"));
    map.put(CommonConstants.ATT_ATTACHMENTS_FOLDERCLASS, ffclass);
    // add file
    IRow row = tab_attachment.createRow(map);

### 13.5.3 Retrieving Attachment Files

If a file folder is checked out by another user, you can still retrieve a copy of the file folder file(s) and save it to your local machine. The IAttachmentFile.getFile() method returns the file stream associated with a row of the Attachments table. The file stream can be for one file or it can be a zipped file stream for multiple files, depending on how many files the associated file folder has. You can also use IAttachmentFile.getFile() to get one or more files directly from a file folder instead of accessing the Attachments table of another business object. If you call getFile() from the file folder object, you return the zipped file stream for all files listed on the Files table. If you call getFile() from a row of the Files table of a file folder, you return a file stream for the specific file associated with that row.

Note: When you use IAttachmentFile.getFile(), only file attachments are included in the returned file stream. URL attachments don't have files associated with them.

This example shows how to retrieve a copy of an attached file.

Example: Getting attachment files

    // Get one or more files associated with the row of an Attachments table or a Files table
    public InputStream getAttachmentFile(IRow row) throws APIException {

InputStream content = ((IAttachmentFile)row).getFile();

return content;

    }
    params.put(DesignConstants.ATT_RELATIONSHIPS_REV_VERSION, versionNum);
    // Get all files associated with a file folder
    public InputStream getAttachmentFiles(IFileFolder ff) throws APIException {

InputStream content = ((IAttachmentFile)ff).getFile();

return content;

    }

If you use IFileFolder.getFile() to return a zipped file stream for all files contained in a file folder, you can extract files from the zipped InputStream using methods of the java.util.zip.ZipInputStream class, as shown in the following example.

Example: Extracting files from a zipped file stream

    static void unpack(InputStream zippedStream) throws IOException {

ZipInputStream izs = new ZipInputStream(zippedStream);

ZipEntry e = null;

while ((e = izs.getNextEntry()) != null) {

if (!e.isDirectory()) {

FileOutputStream ofs = new FileOutputStream(e.getName());

byte[] buf = new byte[1024];

int amt;

while ((amt = izs.read(buf)) != -1) {

ofs.write(buf, 0, amt);

}

ofs.close();

}

}

zippedStream.close();

    }

The Agile API provides no direct method for opening an attachment file. However, you can retrieve a file and then have your program open it in a separate application or display it in a browser window.

### 13.5.4 Deleting Attachments and File Folders

To delete a file folder, which may contain multiple files, use the IDataObject.delete() method. You must have the Delete privilege for file folders to be able to delete them. For more information about deleting objects, see Deleting and Undeleting Objects on page 36.

Note: Deleting a file folder does not automatically remove its associated files from the file server. The Agile PLM administrator is responsible for purging deleted files.

To delete a row from the Attachments table of a business object, use the ITable.removeRow() method. For more information, see Removing Table Rows on page 86. Removing a row from the Attachments table does not delete the associated file folder. You cannot delete a row from the Attachments table in the following situations:

- The parent object is an Item whose revision is incorporated.

- The selected attachment is currently checked out.

### 13.5.5 Working with Thumbnails

Agile PLM supports adding small static graphical images (thumbnails) to key objects which either represent graphical objects or require images. For example, documents attached as files such as Excel worksheets, text files, PDF files, CAD files and so on, can have associated thumbnail images.. Thumbnails display scaled down versions of these files and, in the case of Part objects, show how they relate to each other.

The SDK supports the following Thumbnail-related functions:

- Regenerating Thumbnails

- Sequencing Thumbnails

- Setting Master Thumbnails

- Generating Thumbnails while adding Files to Attachments tab

#### 13.5.5.1 Accessing Thumbnails

Agile SDK provides the IThumbnailContainer interface for generalized access to thumbnail-related operations for file folder and business objects. This interface is supported by the following API objects:

- IFileFolder object

- IItem object

- IManufacturerPart object

For IFileFolder objects, set the applicable version using IFileFolder.setCurrentVersion before calling the above APIs. The default version is LATEST_VERSION. For IItem or IManufacturerPart objects, use the revision that is already set on these objects.

The following example gets thumbnail details from TitleBlock of an IItem or IFileFolder object .

Example: Getting thumbnail details from TitleBlock of IItem or IFileFolder objects

    IItem dataObj =
    ITable titleBlockTable =

(IItem)session.getObject(IItem.OBJECT_TYPE, "P00015");

dataObj.getTable(TableTypeConstants.TYPE_PAGE_ONE);

    Iterator i =
    while (i.hasNext()) {

titleBlockTable.getTableIterator();

IRow row = (IRow)i.next();

Object thumbnailIDDetails =

row.getValue(ThumbnailConstants.ATT_THUMBNAIL_ATTACHMENT_TAB);

IAgileList[] nodes =

((IAgileList)thumbnailIDDetails).getSelection();

for(int ii=0; ii<nodes.length; ii++) {

IAgileList childNode = nodes[ii];

IThumbnailID thumbnailID = (IThumbnailID)childNode.getValue();

}

}

#### 13.5.5.2 Regenerating Thumbnails

Regenerating a thumbnail means generating a thumbnail for an existing (generated) thumbnail for file folder and item objects. This feature is of particular interest in assembly structures where a change in the child of the assembly structure is reflected in the thumbnail after the thumbnail is regenerated.

Agile SDK provides the IThumbnailContainer.generateThumbnail(IThumbnailID) API for this purpose. When invoked, it will generate and return a new thumbnail. In case of IFileFolder objects, API will use the current version of the object. For IItem or IManufacturerPart objects, it will use the current revision of the object. An APIException is thrown when the API fails to regenerate the thumbnail for the specified thumbnailID parameter.

Example: Regenerating a thumbnail for an IFileFolder object

    IFileFolder ff =

(IFileFolder)session.getObject(IFileFolder.OBJECT_TYPE, "FOLDER00037");

ff.setCurrentVersion(new Integer(1));

IThumbnailID oldThumbnailID = "";

//get this id from row of supported tables like Title Block

ff.generateThumbnail(oldThumbnailID);

Regenerating a thumbnail for an IItem object

IItem itemObj =

(IItem)session.getObject(IItem.OBJECT_TYPE, "P00015");

IThumbnailID oldThumbnailID = "";

    //get this id from row of supported tables like Title Block

itemObj.generateThumbnail(oldThumbnailID);

#### 13.5.5.3 Setting Master Thumbnails

In Agile PLM, a file folder object is represented by a thumbnail file which can contain several files in its Files tab. Using SetMasterThumbnai l, a user can decide which row in the Files tab will represent the selected thumbnail's file folder.

SDK provides the setMasterThumbnail API to set master thumbnails on file folder objects. An exception is thrown if the function fails to set the master thumbnail represented by the parameter masterRow.

Example: Setting a master thumbnail

    IFileFolder ff =
    ff.setCurrentVersion(new Integer(1));
    ITable attachmentTable =

(IFileFolder)session.getObject(IFileFolder.OBJECT_TYPE, "FOLDER00037");

ff.getTable(FileFolderConstants.TABLE_FILES);

    Iterator i =
    while (i.hasNext()) {
          IRow row = (IRow)i.next();
          IRow masterRow = null;

attachmentTable.getTableIterator();

//set one of the rows as the master row

    }
    ff.setMasterThumbnail(masterRow);

#### 13.5.5.4 Replacing Thumbnails

You can replace an Agile PLM generated thumbnail with a user provided image for file folder and item objects. The SDK provides the following API for this purpose.

IThumbnailID replaceThumbnail (IThumbnailID oldThumbnailID, byte[] bytes) throws APIException

This API will replace the thumbnail referred to in oldThumbnailID with the image file referred to in the input stream. That is, it will return the thumbnailID of the replaced thumbnail.

For IFileFolder objects, the API will use the version that is already set on the object. For IItem or IManufacturerPart objects, it will use the revision that is already set on the object. An APIException is thrown if it fails to replace the thumbnail specified in the oldThumbnailID parameter.

Example: Replacing a thumbnail for an IFileFolder object

    IFileFolder ff =
     ff.setCurrentVersion(new Integer(1));
     IThumbnailID oldThumbnailID = "";
    //get this id from row of supported tables like Title Block
     String filePath = "C:\\Earth.bmp";
     File file1_tmp = new File(filePath);
     byte[] b1 = new byte[(int)file1_tmp.length()];
     FileInputStream fileInputStream = new FileInputStream(file1_tmp);
     fileInputStream.read(b1);
     IThumbnailID newThumbnailID = ff.replaceThumbnail(oldThumbnailID, b1);

(IFileFolder)session.getObject(IFileFolder.OBJECT_TYPE, "FOLDER00037");

Example: Replacing a thumbnail for an IItem object

    IItem itemObj =
     IThumbnailID oldThumbnailID = "";
    //get this id from row of supported tables like Title Block
     String filePath = "C:\\Earth.bmp";
     File file1_tmp = new File(filePath);
     byte[] b1 = new byte[(int)file1_tmp.length()];
     FileInputStream fileInputStream = new FileInputStream(file1_tmp);
     fileInputStream.read(b1);
     IThumbnailID newThumbnailID = itemObj.replaceThumbnail(oldThumbnailID, b1);

(IItem)session.getObject(IItem.OBJECT_TYPE, "P00015");

#### 13.5.5.5 Sequencing Thumbnails

When Web Client users add attachment files to business objects, they can also set the order (sequence) of their appearance in the Thumbnail Navigator. Agile PLM provides the setThumbnailSequence API to enable this feature in the SDK. For IItem or IManufacturerPart objects, the API will use the revision that is already set on the object. The API will sort (sequence) the order of appearance using the thumbnailIDs parameter. An exception is thrown if the function fails to set the master thumbnail.

Example: Sequencing thumbnails

    IItem itemObj =
    IThumbnailID[] thumbnailIDs = null;
    //get this id from row of Title Block table
     IThumbnailID[] newSeqOfThumbnailIDs = null;
    //generate new order using thumbnail IDs
     itemObj.setThumbnailSequence(newSeqOfThumbnailIDs);

(IItem)session.getObject(IItem.OBJECT_TYPE, "P00015");

#### 13.5.5.6 Generating Thumbnails while Adding Files to Attachments Tab

There are no APIs specifically for this purpose. When you add a file to the Attachments tab of an Item, a thumbnail is generated for that file provided thumbnail support is enabled in the Web Client.

### 13.5.6 Working with Design Objects

A Design object is a business object that specifies one or more URLs or files stored in Agile PLM's File Management Server. It contains information about the binary files attached to it. Similar to other Agile PLM business objects, Design objects appear in Agile PLM's class hierarchy as a separate base class.

Design class objects are used with Agile PLM's Engineering Collaboration (EC ) module which is used to manage CAD data in Agile PLM. Objects created in this class have many of the same properties and behaviors of File folders. In Java Client, users with administrator privileges can enable other users to open and work with Design objects. Agile PLM users can then access and work exclusively with these objects in Web Client.

Agile SDK supports the following Design object-related functions:

- Managing (adding, removing, getting, and editing) version specific Relationships between two Design objects

- Using where-used queries for Design object deployments in Agile PLM Class structures. For information about where-used queries, see Creating a Where-Used Query on page 67.

#### 13.5.6.1 Adding and Loading Design Objects

To create or get an IDesign object, you can useIAgileSession.createObject() or IAgileSession.getObject(). The following examples show the different methods provided by the SDK to create and get Design objects.

Example: Creating a Design by class name

    IDesign des = (IDesign)

m_session.createObject("Design", "DESIGN00133");

Example: Creating a Design by class ID

    IDesign des = (IDesign)

m_session.createObject(FileFolderConstants.CLASS_DESIGN, "DESIGN00133");

Example: Creating a Design by IAgileClass reference:

    IDesign des = (IDesign)

m_session.createObject(desClass, "DESIGN00133");

Example: Loading a Design object

    IDesign des = (IDesign)

m_session.getObject(IDesign.OBJECT_TYPE, "DESIGN00133");

#### 13.5.6.2 Managing Version Specific Relationships between Design Objects

Agile SDK supports the following version specific Relationships functions between Design objects:

Note These version specific functions only apply to Design objects.

- Adding version specific relationships between Design objects

- Removing version specific relationships between Design objects

- Getting version specific relationships for specific versions of Design objects

- Editing version specific relationships for Design objects

#### 13.5.6.3 Adding Relationships for Specific Versions of Design Objects

The SDK provides the following API to add relationships between two specific versions of Design objects:

IDesign.addVersionSpecificRelationship(Object versionNum, IDesign relatedDesign, Object relatedVersionNum)

The parameters are:

- versionNum - This an integer showing the version number of this Design object.

- relatedDesign - The Design object you are creating the Relationships for.

- relatedVersionNum - This an integer showing the version number of the Design object you are creating the Relationship for.

An APIException is thrown if the version specific relationship between the two Design objects was not created.

Alternatively, you can load the object's RelationshipsTable and call createRow(Object params) with the following params:

    HashMap params = new HashMap();
    params.put(DesignConstants.ATT_RELATIONSHIPS_NAME, relatedDesign);
    params.put(DesignConstants.ATT_DESIGN_VERSION, relatedVersionNum);

#### 13.5.6.4 Removing Relationships for Specific Versions of Design Objects

To remove Version Specific Relationships for IDesign:

    IDesign des1 = (IDesign)session.getObject(IFileFolder.OBJECT_TYPE, "DESIGN00001");
    des1.setCurrentVersion(new Integer(4));
    ITable relationshipTable = des1.getRelationship();
    relationshipTable.removeRow(row);

#### 13.5.6.5 Getting Relationships for Specific Versions of Design Objects

To get the Relationships for a specific version of IDesign:

    IDesign des1 = (IDesign)session.getObject(IFileFolder.OBJECT_TYPE, "DESIGN00001");
    des1.setCurrentVersion(new Integer(4)); //set desired version
    ITable relationshipTable = des1.getRelationship();

#### 13.5.6.6 Editing Relationships for Specific Versions of Design Objects

To edit the Relationships for a specific version of the IDesign object:

    IDesign des1 = (IDesign)session.getObject(IFileFolder.OBJECT_TYPE, "DESIGN00001");
    des1.setCurrentVersion(new Integer(4));
    ITable relationshipTable = des1.getRelationship();
    HashMap mapForUpdate=new HashMap();
    HashMap rowUpdateMap = new HashMap();
    rowUpdateMap.put(DesignConstants.ATT_DESIGN_VERSION, new Integer(1));
    mapForUpdate.put(row1, rowUpdateMap);
    relationshipTable.updateRows(mapForUpdate);

#### 13.5.6.7 Purging Specific Versions of Design Objects

The SDK provides the IDesign.purgeVersions(Object[] versions) API for purging specific versions of Design objects and relevant versions of its child objects. The versions parameter, an integer value, specifies the version number you want purged. An exception is thrown if the API fails to purge the object.

#### 13.5.6.8 Searching Design Object Deployments with Where-Used Queries

The Structure tab for Design objects enables users to create structures of different Design objects having different versions. The SDK supports searching for Design object usage in Agile PLM Class Structures for the latest checked in versions and all checked in versions with the following queries and query constants:

- WHERE_USED_IN_STRUCTURE_ONE_LEVEL_LATEST_CHECKEDIN - This WHERE_USED query returns the LATEST version of the immediate parent of the Design object which uses the input Design object as a child in the design structure. The constant QueryConstants.WHERE_USED_IN_STRUCTURE_ONE_LEVEL_LATEST_CHECKEDIN supports this search.

- WHERE_USED_IN_STRUCTURE_ALL_LEVEL_LATEST_CHECKEDIN - This WHERE_USED query returns ALL versions of the immediate parent of the Design object which uses the input Design object as a child in the design structure. The constant QueryConstants.WHERE_USED_IN_STRUCTURE_ONE_LEVEL_ALL_CHECKEDIN supports this search.

You can find code samples using QueryConstants in SDK_samples.zip folder. To access this file, see the Note in Client-Side Components on page 2. These are the Javadoc generated HTML files in the documentation folder.

The two searches and their respective results are explained with the aid of the following illustration. It shows the Design objects and level one structures. The search parameter Title Block.Number includes the number 11.

Figure 13-3 Design objects and search results