Prune deleted folders of repo diff report using Java

Sometimes the contents of deleted folders are not relevant. Just showing the top level deleted folder or dir is enough. For example, in a branch diff against trunk, folder moves are shown as adds and deletes. But in a diff report, the whole sub node of the deleted folder is output.

In the particular report I was creating everything under the deleted directories was just noise. How to prune that?

BTW, I searched the SVN docs and found no support for this kind of pruning. I also looked at the Git docs to see if it was different, but also found no mention of this use-case.

Example scenario:

d1 (modified)
├ d2 (deleted)
   ┝ d3 (deleted)

If we create a deleted prune report of the above, it would contain, d1 modified, and d1/d2 deleted. It would not contain d1/d2/d3.

The algorithm in psuedocode

Jan 8, 2015: I found a use-case where this algorithm does not work when used with an actual Subversion repository being accessed by SvnKit. Not due to SVNKit of course. Will update when fixed.

 

boolean deleting := false
String deletePath = “”

for each path{
    if deleting then 
            if path starts with deletePath then
                skip path
            else
                deleting = false
                deletePath = “”
                result.add(path)
            endif       
    else
        if path is directory and it is deleted then
            deleting = true
            deletePath = path
        endif
        
        result.add(path)
        
    endif
}

This can be implemented at the script level, for example, using Groovy or some other language.

SvnKit Scenario

In an Subversion Console application I wrote using ReactJS, I used SvnKit at the server side to interface to the Subversion server. Future blog post will discuss this single-page app. One of the React components was a Diff table.

Using the SvnKit library I generated an svn diff summary list. This is a list of SvnDiffStatus objects: List. Among other things, these object contain a path string of the respective resource.

To prune the excess deleted folders I first attempted to create an Object tree structure, then I gave up on that (too slow), finally a co-worker suggested I just sort the paths and do a simple contains comparison. Wow, how did I miss that? I guess I was sidetracked by “Objects” and didn’t use simple scripting techniques.

So to prune the folders we sort and prune the results of an SvnDiffSummary object, svnDiff::
List list = pruneDeletedFolders(sortDiffList(((Receiver)svnDiff.getReceiver()).getEntries()));

Where Receiver is an implementation of ISvnObjectReceiver.

(The SvnDiff entries may already be sorted, but the SvnKit API docs do not specify this).

Sort the diff list using Guava

	private List<SvnDiffStatus> sortDiffList(List<SvnDiffStatus> list) {
		return new Ordering<SvnDiffStatus>() {
			@Override
			public int compare(SvnDiffStatus left, SvnDiffStatus right) {
				return left.getPath().compareTo(right.getPath());
			}
		}.sortedCopy(list);
	}

prune the list

	private List<SvnDiffStatus> pruneDeletedFolders(List<SvnDiffStatus> list){
		String deletedPath = "";
		boolean isDeleting = false;
		
		List<SvnDiffStatus> prunedList = new ArrayList<>();

		for (Iterator<SvnDiffStatus> iterator = list.iterator(); iterator.hasNext();) {
			SvnDiffStatus diff = iterator.next();
			String path = diff.getPath();
			if (isDeleting) {
				if (path.startsWith(deletedPath)) {
					// skip this diff
				} else {
					isDeleting = false;
					deletedPath = "";
					prunedList.add(diff);
				}
			} else {
				if (isDirectory(diff) && isStatusDeleted(diff)) {
					isDeleting = true;
					deletedPath = path;
				}

				prunedList.add(diff);
			}
		}

		return prunedList;

	}

Links

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Control multiple PCs with single keyboard and mouse

A software KVM approach is sometimes a good solution. While setting up a new PC I had to access the older PC. I used to use Synergy, but could not find if that software is still around.

Now I’m using ‘Mouse without Borders’. It allows me to control the two PCs, Windows 7 and Windows 10. Works very well. It also shares the keyboard and copy and paste.

Links

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Windows 7 Startup Repair can take a very very long time

My PC crashed. This time it boots into Startup Repair screen. For those who haven’t experienced this fun example of high technology, it is a dialog box that says, a check for problems will take a few minutes and a repair could even last an hour. The horror of this dialog is that you don’t know if it is working or just hung. It uses a animated “progress” bar. The blue just goes left to right and repeats.

It kept running so long I just stopped the PC (long power button press). Note, many on web warn about interrupting this repair! I’m so tough, I interrupted it multiple times; it ain’t the boss of me. Anyway, I gave up, let it keep running. I figured the hard drive light is blinking every few seconds and then a few rapid flashes. Maybe it is doing some bit twiddling on the hard drive.

It took approximately 72 hours!!!! At the end I was able to log into Win 7. Of course, the Law of Large Numbers, five minutes later the mailman dropped off a new PC I ordered online.

Ok, I logged in. It’s working. I take back all the trash talk on Windows. But, wait … I said to myself, hmmm, if there were disk errors, I should run a checkdisk and sector scan. So I pick those on the C drive and reboot. Now no screen at all, and the hard drive light is on all the time. Oy veah.

Let me work on the new Windows 10 PC. A cheap box, but the processor is not too shabby An Intel Core i5-6400 CPU @ 2.70Ghz.

HomeGroup can’t connect. There is already an existing old HomeGroup. WTH!

Updates
1. The checkdisk and sector scan finished. System still running. Yes, these two took hours, and worse than a progress idiot box is no UI at all.

2. HomeGroup. Followed some steps given on web. Did not work. I found these instructions that did work: http://answers.microsoft.com/en-us/windows/forum/windows_7-networking/deleting-old-homegroup/66cf9557-9130-4582-9d7b-4f8689699bf7?auth=1.
BurrWalnut writes: “Go to Control Panel > All Control Panel Items > Homegroup > Leave the Homegroup and click Leave the Homegroup. If all the computers leave the Homegroup, it will no longer exist, so it‘s effectively deleted and can be setup again.” Weird.

The same person writes: “A home group with no connected computers is like a god with no worshipers; neither can exist.” That is deep!!!

3. The HP Hardware Diagnostic Tools just ran and I got a failure of the SMART Short Self Test Failed (Error Code HD521-2W). Hard drive failure is imminent.

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Progress indicators should also indicate that change is occurring

If an application is showing a progress bar of some kind it means it is doing something. If that something is doing some work not just waiting for a network response, the app should indicator the current work it is doing.

Instead some apps use an “idiot light” approach. Case in point. My PC crashed. Startup Repair is running. All it shows is a progress indicator, not one based on completion status or time left, just a blue rectangle cycling by. Its been running for hours. Is is still doing anything, is it hung, is there hope?

Sure, for many apps, actual work status output is redundant and not useful to the “average” user. So when should more information be shown? When the elapsed time is over some threshold. Or if a user wants more information, they can signal that to the app. Many applications use this approach. Why something so fundamental as repairing disks doesn’t do this is very puzzling.

The same dialog box is used in other parts of Windows 10, like when creating a Restore Point, so we still have the same “idiot light” User Experience Design (UxD).

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Sonos unable to add folder due to Kapersky firewall

On laptop I tried to add a local folder on C drive to Sonos. The Sonos controller on the same laptop gave the 1002 error code. This is a timeout?

Tried to find solution on web. Firewall had no issues and anti virus software wasn’t doing anything strange. Just in case, I turned off file scanning. Still have issue. Turned off the Windows firewall; still have problem.

Long story short, Kapersky has a firewall running, doh! Turned that off, Sonos can read local folder. Makes no sense. Why would a firewall block a local app from local folder? Probably because the local Sonos controller has to update the local network with that folder so that other controllers in house can also use it.

Links
Sonos support page on error code 1002

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Windows mouse pointer disappears on second monitor

Started happening recently. Maybe an upgrade or some new glitch in system caused it. Experimenting, and found that turning on the Mouse’s Pointer trails eliminated the problem.

System

  • Windows 7 Professional
  • UltrMon 3.2.2
  • HP PC p6230y
  • AMD Phenon II X4 810 Processor 2.60 GHz
  • System Mouse driver

Related Links

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Java Properties dupe key detect using subclass

And now for the final word on this ‘problem’, the simplest approach is just to subclass the Properties class. The Properties class extends HashTable and uses it’s put(key,value) method. So we just override that method to do the duplicate key detection.

This was also addressed using Groovy scripting in this post, “Remove duplicate lines using Groovy”.

Source also available as a gist.

package com.octodecillion.util;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * Detect duplicate properties when loading Properties from stream.
 * <p>
 * 
 * @author jbetancourt
 * @since Nov 18, 2015
 */
public class DuplicatePropertyDetectorWithSubClass {

	/**
	 * Detect duplicate keys in Properties resource.
	 * <p>
	 * @see Properties#load(Reader)
	 * 
	 * @param reader The reader is <b>NOT</b> closed.
	 * @return Map Map where map key is duplicated key and list is values of those keys
	 * @throws IOException reading the stream
	 * @throws Exception 
	 */
	public Map<String, List<String>> load(Reader reader) throws Exception {
		if(reader==null){ throw new NullPointerException("reader cannot be null");}
		final Map<String, List<String>> results = new HashMap<>();
		
		Properties props = new Properties(){
			private static final long serialVersionUID = 1L;

			@Override
			public synchronized Object put(Object key, Object value) {
				if (this.containsKey(key)) {
					List<String> list = new ArrayList<>();
					if (results.containsKey(key)) {
						list = results.get(key);
					} else {
						results.put((String) key, list);
					}

					list.add((String) value);
				}			
				
				return super.put(key, value);
			}
		};
		
		props.load(reader);
		return results;
	}
}

Links
Nice tutorial on Anonymous classes: Anonymous Classes

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Java Properties dupe key detect using CGLIB

In a prior post I used Javassist for Dynamic Proxying of Properties class’s load method. This allowed detection of duplicate keys in a Properties file.

In this post I use the Class Generation Library (CGLIB).

Listing 1, Source available at Gist

package com.octodecillion.util;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * Detect duplicate properties when loading Properties from stream.
 * <p>
 * This version uses <a href="https://github.com/cglib/cglib/wiki">CGLIB</a> Dynamic Proxy support.
 * 
 * @author jbetancourt
 * @since Nov 18, 2015
 */
public class DuplicatePropertyDetectorWithCGLIB {

    /**
     * Detect duplicate keys in Properties resource.
     * <p>
     * @see Properties#load(Reader)
     * 
     * @param reader The reader is <b>NOT</b> closed.
     * @return Map Map where map key is duplicated key and list is values of those keys
     * @throws IOException reading the stream
     * @throws RuntimeException for any issue with proxying
     */
    public Map<String, List<String>> load(Reader reader) throws IOException {
        if(reader==null){ throw new NullPointerException("reader cannot be null");}
        
        final Map<String, List<String>> dupeResults = new HashMap<>();
        final String put_method_name = "put";
        
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Properties.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object object, Method method, 
              Object[] args, MethodProxy proxy) throws Throwable {
                if(method.getDeclaringClass() != Object.class && isPut(method.getName())) {
                    System.out.println("Putting: " + args[0]);
                    String key = (String) args[0],value = (String) args[1];
                    if (((Properties) object).containsKey(key)) {
                        List<String> list = new ArrayList<>();
                        if (dupeResults.containsKey(key)) {
                            list = dupeResults.get(key);
                        } else {
                            dupeResults.put(key, list);
                        }

                        list.add(value);
                    }
                }
                
                return proxy.invokeSuper(object, args);
            }
            
            private boolean isPut(String name) {
                return name.equals(put_method_name);
            }
        });
        
        Properties props = (Properties) enhancer.create();
        props.load(reader);

        return dupeResults;
    }
}

Links

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Java Properties dupe key detect using Javassist

In a prior post, I used Commons Configuration to detect duplicate properties in a Properties file. The code was very simple.

Here I present a solution using a Dynamic Proxy class. A complexity is that though Properties extends the Map interface, that interface does not declare a load method. And, the JDK Dynamic Proxy support can only be used for interfaces, not concrete classes. The Javassist library can be used for this purpose as shown in listing 1 below.

Listing 1, dupe detect with Javassist Source Gist

package com.octodecillion.util;

import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;

/**
 * Detect duplicate properties when loading Properties from stream.
 * <p>
 * This version uses <a href="https://jboss-javassist.github.io/javassist/">Javassist</a> Dynamic Proxy support.
 * 
 * @author jbetancourt
 * @since Nov 18, 2015
 */
public class DuplicatePropertyDetectorWithJavassist {

    /**
     * Detect duplicate keys in Properties resource.
     * <p>
     * @see Properties#load(Reader)
     * 
     * @param reader The reader is <b>NOT</b> closed.
     * @return Map Map where map key is duplicated key and list is values of those keys
     * @throws IOException reading the stream
     * @throws RuntimeException for any issue with proxying
     */
    public Map<String, List<String>> load(Reader reader) throws IOException {
        if(reader==null){ throw new NullPointerException("reader cannot be null");}
        
        final Map<String, List<String>> dupeResults = new HashMap<>();
        final String put_method_name = "put";
        
        try {
            ProxyFactory factory = new ProxyFactory();
            factory.setSuperclass(Properties.class);
            factory.setFilter(new MethodFilter() {
                @Override
                public boolean isHandled(Method m) {                    
                    return m.getName().equals(put_method_name);
                }
            });

            ((Properties) factory.create(new Class<?>[0], new Object[0], 
              new MethodHandler() {
                @Override
                public Object invoke(Object self, Method thisMethod, 
                    Method proceed, Object[] args) throws Throwable {

                    String key = (String) args[0],value = (String) args[1];

                    if (((Properties) self).containsKey(key)) {
                        List<String> list = new ArrayList<>();
                        if (dupeResults.containsKey(key)) {
                            list = dupeResults.get(key);
                        } else {
                            dupeResults.put(key, list);
                        }

                        list.add(value);
                    }

                    return proceed.invoke(self, args);
                }

            })).load(reader);

        } catch (NoSuchMethodException | IllegalArgumentException 
                | InstantiationException | IllegalAccessException
                | InvocationTargetException e) {
            throw new RuntimeException(e);
        }

        return dupeResults;
    }    
}

Links
Javassist

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Java dupe key Properties detect using Commons Configuration

Java Properties files can contain duplicate key entries. This can be a problem since the Java Properties loader will just keep the last value parsed.

Not a problem for small applications, but when property files grow in number and size this can become a maintenance issue. Fortunately there are many solutions for this (I hope). This post will present a very simple method that performs duplicate key detection.

When first confronted with dupe detection goal, reading the Properties file directly and determining dupes using a HashMap sounds simple. However, you would have to duplicate the Properties file format parsing. That can get complex. For example, a property entry may use “:” or “=” to specify the key, value pair. Plus, you would have to handle the line continuations feature, and other requirements.

Commons Configuration makes this simple since its Properties loader will create lists for any duplicate property key. Thus, to detect duplicates, just find which property values are lists, see listing 1 below.

Listing 1, Duplicate key detection using Commons Config Also available as a Gist

package com.octodecillion.util;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;

/**
 * Detect duplicate properties when loading Properties from stream.
 * <p>
 * 
 * @author jbetancourt
 * @since
 */
public class DuplicatePropDetectWithCommonsConfig {

    /**
     * Detect duplicate keys in Properties resource.
     * <p>
     * <a href="">Commons Config</a> is used.
     * {@link Properties} class.
     * 
     * @see Properties#load(Reader)
     * 
     * @param reader The reader is NOT closed.
     * @param delimiterParsingDisabled 
     * @return Map of keys that are duplicated with a list value of each value of duplicate. 
     * @throws ConfigurationException 
     */
    @SuppressWarnings("unchecked")
    public Map<String, List<String>> loadWithConfig(Reader reader, boolean delimiterParsing) 
      throws ConfigurationException {
        PropertiesConfiguration config = new PropertiesConfiguration();
        final Map<String, List<String>> results = new HashMap<>();

        config.setDelimiterParsingDisabled(delimiterParsing);
        config.load(reader);

        Iterator<String> keys = config.getKeys();
        while (keys.hasNext()) {
            String theKey = keys.next();
            Object valueObject = config.getProperty(theKey);
            if (!(valueObject instanceof String)) {
                results.put(theKey, (List<String>) valueObject);
            }
        }

        return results;
    }
    
    /**
     * @author jbetancourt
     *
     */
    @RunWith(Enclosed.class)
    public static class DuplicatePropDetectWithCommonsConfigTest {
        private static final String DATA1_PROPERTIES = "/Data1.properties";

        /**
         * @throws ConfigurationException
         */
        @Test
        public void testLoadFileWithConfig() throws ConfigurationException {

            InputStream is = this.getClass().getResourceAsStream(DATA1_PROPERTIES);
            Assert.assertNotNull(is);

            DuplicatePropDetectWithCommonsConfig detector = new DuplicatePropDetectWithCommonsConfig();
            Map<String, List<String>> map = detector.loadWithConfig(new InputStreamReader(is), true);
            Assert.assertEquals(1, map.size());

            Assert.assertTrue(map.containsKey("three"));

        }
    }
}

Links

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

The celebrated man in the street

%d bloggers like this: