Monday, November 27, 2017

Java String.replaceAll() -- replace only the Matched String not the REGEX

Every Java programmer must have used the method,


String.replaceAll();


Usage Example


    "this is a test and that is another test".replaceAll("test", "rule");


the output of this execution will be:
"this is a rule and that is another rule"

Twist!!!

The usage of the method seems to be straightforward. However its not.

The first parameter to the method is actually a regular expression. You may fall into it, if the first argument has some special character which has different meaning in REGEX world.

What if you still want to match the expression as whole instead of as regex? You will need to convert it into String literal. You can do this by placing your expression in between Regex Quoted Literals. i.e. \Q <param> \E.


Example:


String test = "<span class=\"colorful\">This is a colorful text.</span>";
String replace_start = "<span class=\"colorful\">";
String replace_end = "</span>";

String expectedOutput = "This is a colorful text.";



Without Quoted Literals:


    String result = test.replace(replace_start, "").replace(replace_end, "");
    Assert.assertEquals(expectedOutput, result);

// This will fail because result = "<span class=\"colorful\">This is a colorful text.</span>"

With Quoted Literals


    String result = test.replace("\\Q" + replace_start + "\\E", "").replace("\\Q" + replace_end + "\\E", "");
    Assert.assertEquals(expectedOutput, result);

This will pass.







Tuesday, November 21, 2017

Get List of Links of All Shared Files From Your Dropbox (Using Java API)

Recently I had to extract the links of all the files shared in my dropbox.

Following was the first approach I used.

public List getLinks(DbxClientV2 client) throws DbxException {
    List result = new ArrayList();

    ListSharedLinksResult sharedLinksResult = client.sharing().listSharedLinks();
    for (SharedLinkMetadata slm : sharedLinksResult.getLinks()) {
      result.add(slm.getUrl());
    }

    return result;
  }

At first sight, this method seems to work perfectly fine. However, there is a glitch in the approach. The method

ListSharedLinksResult sharedLinksResult = client.sharing().listSharedLinks();

actually doesn't return all the result. It returns few items at a time. We can know whether there are more items or not by using

sharedLinksResult.getHasMore();
which returns True if there are more items left, False otherwise.

So if there are more items left, then we need to extract them as well.

sharedLinksResult has a cursor, which can be used to extract additional items. This can be done using:

client.sharing().listSharedLinks(new ListSharedLinksArg((String)null, sharedLinksResult.getCursor(), true));

But its not as easy as it looks. The problem is that the overloaded method listSharedLinks(ListSharedLinksArg) is not a Public Method. So you cannot you use just anywhere in your code.

Hack!!!

I managed to use this, by placing my class into the same package where the class DbxUserSharingRequests lie i.e. com.dropbox.core.v2. Obviously, this is not a clean approach.

Solution

As it turns out, the solution is to use

DbxUserSharingRequests.listSharedLinksBuilder()

where we can set the Cursor from sharedLinksResult. Following is my final solution:

public List getLinks(DbxClientV2 client) throws DbxException {
    List result = new ArrayList();

    ListSharedLinksResult sharedLinksResult = client.sharing().listSharedLinks();

    while(true) {
      for (SharedLinkMetadata slm : sharedLinksResult.getLinks()) {
        result.add(slm.getUrl());
      }

      if (sharedLinksResult.getHasMore()) {
        sharedLinksResult = client.sharing().listSharedLinksBuilder().withCursor(sharedLinksResult.getCursor()).withDirectOnly(true).start();
      } else {
        break;
      }
    }
    return result;
  }