Some of the Windows users, like me, who have switched to Eclipse Juno (Eclipse 4.2) might not have liked the theme that ships as default. Specially, the piece around the toolbar backgrounds, the code editing theme, the absence of left border along side line numbers, and the overtly flashy UI containers.

To put in plain simple words: Eclipse Juno theme actually sucks. All the hype around CSS based styling might be very good technically, and a step forward, but per UX standards it sucks, and it sucks hard.

But we have a quick workaround: For those who wish to move to using the Eclipse Indigo looks, a version prior, simply, rename the folder,
plugins\org.eclipse.platform_4.2.0.v201206081400\css

to something like,
plugins\org.eclipse.platform_4.2.0.v201206081400\css.old

And you are done! Launch your eclipse and enjoy the old theme.

Note: Make sure Eclipse is not-running when making this change.

Hope this helps.

written by Sandeep Gupta

Monday, August 13, 2012 at 9:53 AM

I am pleased to announce the immediate availability of Pepmint, a Java wrapper over the Python Pygments library in Maven Central. Use the following to include it as a dependency,

<dependency>
    <groupId>com.sangupta</groupId>
    <artifactId>pepmint</artifactId>
    <version>1.1.2</version>
</dependency>

For more information on usage, visit the project page at http://www.sangupta.com/projects/pepmint

written by Sandeep Gupta

Saturday, July 28, 2012 at 11:46 AM

If you are working with HTML5 Canvas element and are looking to save the generated PNG file back on to the server via Java - it is not as easy as saving the byte array. The reason that the generated PNG data is URL encoded and is prefixed with the dataURI format headers.

    var canvas = document.getElementById('#myCanvas');
    var pngData = canvas.toDataURL('image/png');

The following code will convert the raw bytes by stripping off the headers, and coverting the Base64 encoded data to the raw PNG bytes.

private void saveImage(byte[] pngData) {
 String png = new String(pngData);
 String find = "base64%2C";
 String tokens = png.substring(png.indexOf(find) + find.length());
 String decoded = StringUtils.replace(tokens, "%2F", "/");
 byte[] bytes = new Base64Encoder().decode(decoded);

 // save the generated bytes
 // which can then be read in a BufferedImage
 BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
 
 // mroe code
}

Hope this helps.

written by Sandeep Gupta

Thursday, July 12, 2012 at 2:10 PM

Problem: Given some HTML code, trim it down into valid HTML code that contains text of desired length.

For example:
String s1 = "Text with bold, italic phrases.";
String s2 = trimHTML(s1, 12);
System.out.println(s2);
should return
Text with bo

Solution: For a project of mine, I had to use such a functionality. A quick google did not result in any existing function, and thus I ended up coding the following:

/**
 * Strip the given HTML content to specified text length. All opening
 * tags are then closed to make sure that the HTML is perfectly safe.
 * 
 * Tags such as br are skipped for closing.
 * 
 * @param content the HTML content that you want to trim down
 * @param length the desired length of the text field
 * @return the HTML code that contains text trimmed down to said length
 */
public static String trimHTML(String content, int length) {
 int currentIndex = 0;
 int chosenTextLength = 0;
 String tag;
 Stack tags = new Stack();
 do {
  int index = content.indexOf('<', currentIndex);
  if(index > currentIndex) {
   chosenTextLength += (index - currentIndex - 1);
   currentIndex = index;
  }
  
  if(chosenTextLength >= length) {
   break;
  }
  
  if(index != -1) {
   index = content.indexOf('>', index);
   tag = content.substring(currentIndex + 1, index);
   if(!tag.startsWith("/")) {
    if(tag.endsWith("/")) {
     tag = tag.substring(0, tag.length() - 1);
    }
    
    tags.push(tag.trim());
   } else {
    tag = tag.substring(1);
    do {
     if(tags.size() == 0) {
      break;
     }
     
     String pop = tags.pop();
     if(pop.equalsIgnoreCase(tag)) {
      break;
     }
    } while(true);
   }
   
   currentIndex = index;
  }
  
  if(index == -1) {
   break;
  }
 } while(true);
 
 if(chosenTextLength > length) {
  int subtract = chosenTextLength - length;
  currentIndex = currentIndex - subtract;
 }
 
 if(tags.size() == 0) {
  return content.substring(0, currentIndex);
 }
 
 StringBuilder builder = new StringBuilder(content.substring(0, currentIndex));
 int size = tags.size();
 for(int index = 0; index < size; index++) {
  tag = tags.pop();
  
  if(!"br".equalsIgnoreCase(tag)) {
   builder.append("');
  }
 }
 
 return builder.toString();
}


The code is also available under the Jerry project. You may browse the latest edition of this utility function in the GitHub repository in HtmlUtils.java file.

written by Sandeep Gupta

Wednesday, July 11, 2012 at 1:18 PM

This post is about MergeRepo a small script that allows to merge two different snapshots of the same repository from different SCMs into one.

Recently, I had to collaborate on a project where the code was in Subversion. As we usually use Perforce, we forked a copy of the SVN trunk into Perforce and started our development. Once, the development was complete, we wanted to merge back in Subversion. And now, the nightmare started. SVN showed all files (thousands) to have been modified, due to a change in the timestamp or the change in line-ending character.

The issue with line-ending character was easier to resolve, but the change in timestamp still created havoc. Thus, the need for this tool emerged. I wrote this simple script to ignore timestamp, and EOL character, and merge the removed/added/modified files back into the SVN folder, so that only such files were shown for commit.

The tool allows us to resolve such situations. It compares the two snapshots (folders/trees) and generates the merged repository with the only the modified files, so that it can be checked back into the original line. The tool can be used as,

Usage:    $ java com.sangupta.keepwalking.MergeRepo <previous> <newer> <destination>

          previous      The folder corresponding to the older repository snapshot.
          newer         The folder corresponding to the newer repository snapshot.
          destination   The folder where the updated repository will be created.

The source code for the same is available on my GitHub repository, or below.

/**
 *
 * MergeRepo - allows to merge two different snapshots of the same repository
 * Copyright (c) 2012, Sandeep Gupta
 * 
 * Read more at http://blog.sangupta.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.sangupta.keepwalking;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;

/**
 * Allows to merge two different snapshots of the same repository which are checked
 * in to two different SCM versions.
 *
 * @author sangupta
 */
public class MergeRepo {

 /**
  * @param args
  * @throws IOException 
  */
 public static void main(String[] args) throws IOException {
  if(args.length != 3) {
   usage();
   return;
  }
  
  final String previousRepo = args[0];
  final String newerRepo = args[1];
  final String mergedRepo = args[2];
  
  final File previous =  new File(previousRepo);
  final File newer = new File(newerRepo);
  final File merged = new File(mergedRepo);
  
  if(!(previous.exists() && previous.isDirectory())) {
   System.out.println("The previous version does not exists or is not a directory.");
   return;
  }
  
  if(!(newer.exists() && newer.isDirectory())) {
   System.out.println("The newer version does not exists or is not a directory.");
   return;
  }
  
  final IOFileFilter directoryFilter = FileFilterUtils.makeCVSAware(FileFilterUtils.makeSVNAware(null));
  
  final Collection<File> olderFiles = FileUtils.listFiles(previous, TrueFileFilter.TRUE, directoryFilter);
  final Collection<File> newerFiles = FileUtils.listFiles(newer, TrueFileFilter.TRUE, directoryFilter);

  // build a list of unique paths
  System.out.println("Reading files from older version...");
  List<String> olderPaths = new ArrayList<String>();
  for(File oldFile : olderFiles) {
   olderPaths.add(getRelativePath(oldFile, previous));
  }

  System.out.println("Reading files from newer version...");
  List<String> newerPaths = new ArrayList<String>();
  for(File newerFile : newerFiles) {
   newerPaths.add(getRelativePath(newerFile, newer));
  }
  
  
  // find which files have been removed from Perforce depot
  List<String> filesRemoved = new ArrayList<String>(olderPaths);
  filesRemoved.removeAll(newerPaths);
  System.out.println("Files removed in newer version: " + filesRemoved.size());
  for(String removed : filesRemoved) {
   System.out.print("    ");
   System.out.println(removed);
  }
  
  // find which files have been added in Perforce depot
  List<String> filesAdded = new ArrayList<String>(newerPaths);
  filesAdded.removeAll(olderPaths);
  System.out.println("Files added in newer version: " + filesAdded.size());
  for(String added : filesAdded) {
   System.out.print("    ");
   System.out.println(added);
  }
  
  // find which files are common 
  // now check if they have modified or not
  newerPaths.retainAll(olderPaths);
  List<String> modified = checkModifiedFiles(newerPaths, previous, newer);
  System.out.println("Files modified in newer version: " + modified.size());
  for(String modify : modified) {
   System.out.print("    ");
   System.out.println(modify);
  }
  
  // clean any previous existence of merged repo
  System.out.println("Cleaning any previous merged repositories...");
  if(merged.exists() && merged.isDirectory()) {
   FileUtils.deleteDirectory(merged);
  }
  
  System.out.println("Merging from newer to older repository...");
  // copy the original SVN repo to merged
  FileUtils.copyDirectory(previous, merged);
  
  // now remove all files that need to be
  for(String removed : filesRemoved) {
   File toRemove = new File(merged, removed);
   toRemove.delete();
  }
  
  // now add all files that are new in perforce
  for(String added : filesAdded) {
   File toAdd = new File(newer, added);
   File destination = new File(merged, added);
   FileUtils.copyFile(toAdd, destination);
  }
  
  // now over-write modified files
  for(String changed : modified) {
   File change = new File(newer, changed);
   File destination = new File(merged, changed);
   destination.delete();
   FileUtils.copyFile(change, destination);
  }
  
  System.out.println("Done merging.");
 }
 
 private static void usage() {
  System.out.println("RepoMerge: Command-line tool to merge two snapshots of a single repositories that come from ");
  System.out.println("           different sources. Like you have your older repository in SubVersion and the newer");
  System.out.println("           code in Perforce. Now you want to merge the code and bring SVN back to the level of");
  System.out.println("           Perforce.");
  System.out.println("");
  System.out.println("Usage:    $ java com.sangupta.keepwalking.MergeRepo <previous> <newer> <destination>");
  System.out.println("");
  System.out.println("          previous      The folder corresponding to the older repository snapshot.");
  System.out.println("          newer         The folder corresponding to the newer repository snapshot.");
  System.out.println("          destination   The folder where the updated repository will be created.");
 }

 /**
  * Checks if the file with given path is different in two different folders/branches
  *  
  * @param commonPath
  * @param svn
  * @param perforce
  * @return
  * @throws IOException
  */
 private static List<String> checkModifiedFiles(List<String> commonPath, File svn, File perforce) throws IOException {
  List<String> changed = new ArrayList<String>();
  
  for(String filePath : commonPath) {
   File svnFile = new File(svn, filePath);
   File perforceFile = new File(perforce, filePath);
   
   boolean equal = FileUtils.contentEqualsIgnoreEOL(svnFile, perforceFile, Charset.defaultCharset().name());
   if(!equal) {
    changed.add(filePath);
   }
  }
  
  return changed;
 }

 /**
  * Extract the relative path of the file from the absolute path, given the parent.
  * 
  * @param file
  * @param parent
  * @return
  */
 private static String getRelativePath(File file, File parent) {
  String path = file.getAbsolutePath();
  String parentPath = parent.getAbsolutePath();
  if(path.startsWith(parentPath)) {
   return path.substring(parentPath.length() + 1);
  }
  
  return path;
 }

}

Hope it helps, some one out there!

written by Sandeep Gupta

Friday, May 4, 2012 at 3:09 PM