It's all about the answers!

Ask a question

How to get the file path of a changed file in RTC with JAVA?


0
1
Benjamin Maier (3316) | asked Feb 14 '17, 3:22 a.m.

Hello everyone,

I try to fetch the code to get the full path of a file that got edited in a change set linked to a work item. What I currently try to achieve is that I dont only get the filename, but also the full path.
My customized code (from rsjazz) looks like this:



    public void getConnectedFiles(IWorkspaceManager workspaceManager, IWorkItemClient workItemClient, ILinkManager linkManager, IItemManager itemManager, int workItemID){
        try{
        IWorkItem workItem = workItemClient.findWorkItemById(workItemID, IWorkItem.FULL_PROFILE,
                new SysoutProgressMonitor());
        logInfo("[" + workItem.getId() + "] " + workItem.getHTMLSummary().getPlainText());
        // Find all of the change sets using the link manager to find a
        // special kind of
        // link that crosses between work items and source control change
        // sets using its ID.
        IItemReference workItemReference = linkManager.referenceFactory().createReferenceToItem(workItem);
        ILinkCollection linkCollection = linkManager
                .findLinksByTarget(ILinkConstants.CHANGESET_WORKITEM_LINKTYPE_ID, workItemReference,
                        new SysoutProgressMonitor())
                .getAllLinksFromHereOn();
        logInfo("LinkCollection received ...");

        if (linkCollection.isEmpty()) {

            logInfo("Work item has no change sets.");
        } else {

            // read the list of changes

            List<IChangeSetHandle> changeSetHandles = new ArrayList<IChangeSetHandle>();

            for (ILink link : linkCollection) {

                // Change set links should be item references
                IChangeSetHandle changeSetHandle = (IChangeSetHandle) link.getSourceRef().resolve();
                changeSetHandles.add(changeSetHandle);

                @SuppressWarnings("unchecked")

                List<IChangeSet> changeSets = itemManager.fetchCompleteItems(changeSetHandles, IItemManager.DEFAULT,
                        new SysoutProgressMonitor());
                Set<String> changedFilesAndFolders = new TreeSet<String>();
    //            Set<String> changedFilesAndFoldersPath = new TreeSet<String>();
                for (IChangeSet cs : changeSets) {
                    for (Object o : cs.changes()) {
                        IChange change = (IChange) o;

                        if (change.kind() != IChange.DELETE) {

                            IVersionableHandle after = change.afterState();

                            // Although versionable handles are item

                            // handles, you cannot use the item
                            // manager to fetch the versionable from the
                            // handle. Instead, you use the
                            // versionable manager to do this.
                            IVersionable afterVersionable = workspaceManager.versionableManager()
                                    .fetchCompleteState(after, new SysoutProgressMonitor());

                            // TODO: read path, other MetaData ...

                            changedFilesAndFolders.add(afterVersionable.getName());
                //            changedFilesAndFoldersPath.add(afterVersionable.getParent().toString());



As you can see, I try to get the path in the last line, but it only generates some cryptic code.
What can I do?


Comments
Luca Martinucci commented Feb 28 '17, 5:45 a.m.

If you work with client-side API, once you have retrieved the versionable, you need a workspace to resolve the file path.
Then, you retrieve the component associated to the versionable and can calculate the file path.
See the code snippet in my second answer.


Luca Martinucci commented Mar 13 '17, 11:33 a.m.

Benjamin, did you manage to achieve your scope?
If so, please accept my answer, so other people can leverage it.

Accepted answer


permanent link
Luca Martinucci (942277103) | answered Feb 28 '17, 5:53 a.m.
String filePath = "";
        
for (IWorkspaceHandle wHandle : wHandles) {
       IWorkspace workspace = (IWorkspace) repo.itemManager().fetchCompleteItem(wHandle, IItemManager.DEFAULT, null);
       IWorkspaceConnection wsConn = wm.getWorkspaceConnection(workspace, null);
    // previously I had retrieved the component associated to the versionable
       IComponent component = rawFilesHash.get(versionableHandle);
       IConfiguration configuration = wsConn.configuration(component);
    List<IVersionableHandle> versionableHandleList = new ArrayList<IVersionableHandle>();
    versionableHandleList.add(versionableHandle); 
    List<?> ancestorReports = configuration.locateAncestors(versionableHandleList, monitor);
    
    IAncestorReport iAncestorReport = (IAncestorReport) ancestorReports.get(0);
    List<INameItemPair> reportList = iAncestorReport.getNameItemPairs();
                
    for (INameItemPair iNameItemPair : reportList){
        String temp = iNameItemPair.getName();
        if (temp != null) {
            filePath += "/" + temp ;
        }
    }
    
    if (!filePath.isEmpty()) {
        break;
    }
}


Benjamin Maier selected this answer as the correct answer

Comments
Benjamin Maier commented Mar 06 '17, 5:30 a.m.

Hi Luca,

This method doesnt throw an exception, but also doesnt generate an outcome. I debugged it, and in the step:

List<INameItemPair> reportList = iAncestorReport.getNameItemPairs();

It cant grab any nameItemPairs, so reportList doesnt contain any values. The iAncestorReport is valid. How does this come?


David Lafreniere commented Mar 06 '17, 11:50 a.m.
FORUM MODERATOR / JAZZ DEVELOPER

This means the file does not exist in that stream/component.

The Javadoc for this method is:

    /*
     * Returns the sequence of name-item pairs leading from a root folder to a
     * particular item. An empty sequence means the item is not present. The
     * first element corresponds to a root folder and the last element
     * corresponds to the versionable item itself. The first name-item pair
     * (only) will have a <code>null</code> name (because root folders are by
     * definition uncataloged in any other folder); all item handles within the
     * ancestor report carry state ids.
    
/


Luca Martinucci commented Mar 07 '17, 5:21 a.m.

Yes, actually, in my code snippet, I am cycling over the list of workspaces in the Project Area.
I had retrieved this list previously, and I need to cycle over it because I don't know in which workspaces a file exists.
If you are starting from a change set, you may investigate if it possible to retrieve which workspaces, or streams, contain the change set (i.e. if the change set has been delivered/accepted to a specific workspace or stream).


Benjamin Maier commented Mar 14 '17, 4:15 a.m.

Luca, please see my other answer on how to solve this problem more easily.
Thanks for your inspiration!

4 other answers



permanent link
Benjamin Maier (3316) | answered Mar 14 '17, 4:14 a.m.
edited Mar 14 '17, 8:49 a.m. by Ralph Schoon (56.5k23642)

Hello,

I got empty name pairs because I was in the wrong workspace. I found a way to easily get the corresponding workspace to a changeset, and now it builds the filepath for me! I attached my working code, I hope it can help other people with the same problem.

public static String pathFinder(ITeamRepository repo, IWorkspaceManager wm, IChangeSet cs, IVersionableHandle vh)
            throws TeamRepositoryException {
        String filePath = "";
        IWorkspaceConnection wsc = workspaceConnectionFinder(repo, wm, cs);
        IComponentHandle component = cs.getComponent();
        IConfiguration config = wsc.configuration(component);
        List<iversionablehandle> versionableHandleList = new ArrayList<iversionablehandle>();

    versionableHandleList.add(vh); // ok

    List<IAncestorReport> ancestorReports = config.locateAncestors(versionableHandleList,
            new SysoutProgressMonitor());
    IAncestorReport iAncestorReport = ancestorReports.get(0);
    List<INameItemPair> reportList = iAncestorReport.getNameItemPairs();
    for (INameItemPair iNameItemPair : reportList) {
        String temp = iNameItemPair.getName();
        if (temp != null) {
            filePath += "/" + temp;
        }
    }

    return filePath;

}

public static IWorkspaceConnection workspaceConnectionFinder(ITeamRepository repo, IWorkspaceManager wm, IChangeSet cs)

        throws TeamRepositoryException {
    IWorkspaceSearchCriteria wsSearchCriteria = WorkspaceSearchCriteria.FACTORY.newInstance();
    wsSearchCriteria.setKind(IWorkspaceSearchCriteria.ALL);
    List<IWorkspaceHandle> workspaceHandles = wm.findWorkspacesContainingChangeset(cs, wsSearchCriteria,
            Integer.MAX_VALUE, new SysoutProgressMonitor());
    IWorkspaceConnection wsc = wm.getWorkspaceConnection(workspaceHandles.get(0), new SysoutProgressMonitor());
    return wsc;
}

permanent link
Luca Martinucci (942277103) | answered Feb 27 '17, 10:59 a.m.
edited Mar 14 '17, 8:51 a.m. by Ralph Schoon (56.5k23642)

Hi Benjamin,
some time ago I had your same need, so I developed a server-side extension that gets the paths of the files in a change set.
I hope that this code snippet can help you (I tested it on version 5.0.2):

List changes = (List)changeSet.changes();
for (Iterator changesIterator = changes.iterator(); changesIterator.hasNext();) {
    boolean modifiedElement = false;
    boolean addedElement = false;
    boolean deletedElement = false;
    IChange change = (IChange)changesIterator.next();
    IVersionableHandle changeObj = null;
    Integer changeKind = change.kind();

switch (changeKind) {
   case 1: {
       // added
       addedElement = true;
       changeObj = change.afterState();
   };
   break;
   case 2: {
       // modified
       modifiedElement = true;
       changeObj = change.afterState();
   };
   break;
   case 16: {
       // deleted
       deletedElement = true;
       changeObj = change.beforeState();
   };
   default: {
   };
   break;
}
// file path, starting from the component root
String elementPath = "";
ServiceConfigurationProvider configProvider = ServiceConfigurationProvider.FACTORY.create(findStreamFromEnvironment(deliveryTargetName, repositoryProgressMonitorHandle), changeSet.getComponent());
List nameItemPairs = null;
if (modifiedElement) {
    IAncestorReport reports[] = scmService.configurationLocateAncestors(configProvider, new IVersionableHandle[] {changeObj}, null, repositoryProgressMonitorHandle);
    nameItemPairs = reports[0].getNameItemPairs();
}
if (addedElement) {
    IAncestorReport reports[] = scmService.configurationLocateAncestors(configProvider, new IVersionableHandle[] {changeObj}, null, repositoryProgressMonitorHandle);
    nameItemPairs = reports[0].getNameItemPairs();
}
if (deletedElement) {
    IAncestorReport reports[] = scmService.configurationDetermineAncestorsInHistory(configProvider, new IVersionableHandle[] {changeObj}, null, repositoryProgressMonitorHandle);
    nameItemPairs = reports[0].getNameItemPairs();
}

if (nameItemPairs.isEmpty()) {
    IAncestorReport reports[] = scmService.configurationDetermineAncestorsInHistory(configProvider, new IVersionableHandle[] {changeObj}, null, repositoryProgressMonitorHandle);
    nameItemPairs = reports[0].getNameItemPairs();
}
for (Iterator iterator = nameItemPairs.iterator(); iterator.hasNext();) {
    INameItemPair nameItemPair = (INameItemPair)iterator.next();
    String pathPiece = nameItemPair.getName();
    if (pathPiece != null) {
        elementPath = elementPath.concat("/" + pathPiece);
    }
}
if (elementPath.isEmpty()) {
    elementPath = "<file another="" by="" change="" removed="" set="">";
}

}



Comments
Ralph Schoon commented Feb 27 '17, 11:10 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

 Thanks for sharing Luca!


Benjamin Maier commented Feb 28 '17, 5:21 a.m.

Hi Luca,

Thank you for sharing, unfortunately I need a client sided solution.
Is it possible to adapt this snippets to the client side? Some functions here cant be called..
Thx!


Ralph Schoon commented Feb 28 '17, 5:48 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

 To get the ISCMService see https://rsjazz.wordpress.com/2016/02/04/setting-custom-attributes-for-scm-versionables/ 

Getting the IScmService

Most of the API should work on clients and Luca already hinted you to what is needed.


permanent link
Ralph Schoon (56.5k23642) | answered Feb 14 '17, 5:15 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

 As far as I know, the path information RTC stores in SCM is only relative to the component. It is impossible to get an absolute path, unless the file is loaded onto disc somewhere. 



Comments
Benjamin Maier commented Feb 20 '17, 6:55 a.m.

Hi Ralph,

And how do I get the component to a file?
The links you sent are helpful, but I dont really see a method to implement it correctly.
Maybe I'm just blind.

Thanks!


Ralph Schoon commented Feb 20 '17, 8:01 a.m. | edited Feb 20 '17, 8:03 a.m.
FORUM ADMINISTRATOR / FORUM MODERATOR / JAZZ DEVELOPER

 Since there is few information in what context and why you want to do all this, there is almost nothing one can answer. This is a call you might or might not be able to use. com.ibm.team.scm.common.IScmService.getComponentsContainingVersionable(IWorkspaceHandle, IVersionableHandle, ISynchronizationTimes[], IRepositoryProgressMonitorHandle)


How to get the IScmService is in my blog. And I am aware of the fact that it is really hard to find stuff in the API 


Benjamin Maier commented Feb 27 '17, 5:10 a.m.

Hi Ralph,

We are trying to do this to have an easier overview of the changes happening in our project and to see which files got affected where in which work item. Is there really no easy way or working code snippet to get the path to a file?

I already tested code snippets from other forum posts, but they dont work, for example:
http://stackoverflow.com/questions/14990572/get-the-full-path-of-a-file-at-change-set

In this case, NameItemPair doesnt return a valid value.

We are currently out of idea what to do.


permanent link
Luca Martinucci (942277103) | answered Mar 14 '17, 8:31 a.m.

Benjamin,
your solution is actually an improvement of mine.
By setting those search criteria, you avoid cycling on all workspaces, which is a time-consuming task.

Your answer


Register or to post your answer.