All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.walkmod.walkers.AbstractWalker Maven / Gradle / Ivy

There is a newer version: 3.0.5
Show newest version
/* 
  Copyright (C) 2013 Raquel Pau and Albert Coroleu.
 
 Walkmod is free software: you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 Walkmod is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public License
 along with Walkmod.  If not, see .*/

package org.walkmod.walkers;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.log4j.Logger;
import org.walkmod.ChainWalker;
import org.walkmod.Resource;
import org.walkmod.conf.entities.ChainConfig;
import org.walkmod.conf.entities.TransformationConfig;
import org.walkmod.exceptions.WalkModException;
import org.walkmod.merger.IdentificableNode;
import org.walkmod.merger.MergeEngine;
import org.walkmod.merger.Mergeable;

public abstract class AbstractWalker implements ChainWalker {

   public static final String NAMESPACE_SEPARATOR = "::";

   private List visitor;

   private Object writer;

   private Resource resource;

   private String rootNamespace;

   private ChainConfig chainConfig;

   private Set visitedElements;

   private Collection visitorMessages;

   private static Logger log = Logger.getLogger(AbstractWalker.class);

   public static final String ORIGINAL_FILE_KEY = "original_file_key";

   private List constraintProviders = null;

   public AbstractWalker() {
      this.visitedElements = new HashSet();
      this.visitorMessages = new LinkedList();
   }

   @Override
   public void setVisitors(List visitor) {
      this.visitor = visitor;
   }

   @Override
   public void setConstraintProviders(List constraints) {
      this.constraintProviders = constraints;
   }

   @Override
   public List getConstraintProviders() {
      return constraintProviders;
   }

   protected void visit(Object element, List visitors, List transformations,
         VisitorContext context) throws Exception {
      if (rootNamespace != null && !"".equals(rootNamespace)) {
         String qualifiedName = getResource().getNearestNamespace(element, NAMESPACE_SEPARATOR);
         if (rootNamespace.startsWith(qualifiedName)) {
            return;
         }
      }
      if (visitors.isEmpty()) {
         context.addResultNode(element);
      } else {
         int index = 0;
         for (Object visitor : visitors) {
            boolean isMergeable = transformations.get(index).isMergeable();
            String mergePolicy = transformations.get(index).getMergePolicy();
            if (mergePolicy != null) {
               isMergeable = true;
            }
            if (isMergeable && mergePolicy == null) {
               mergePolicy = "default";
            }
            Method[] methods = visitor.getClass().getMethods();
            List restVisitors = visitors.subList(index + 1, visitors.size());
            List restTransformations = transformations.subList(index + 1, transformations.size());
            Set visitedTypes = new HashSet();
            for (int j = 0; j < methods.length; j++) {
               if (methods[j].getName().equals("visit")) {
                  Class type = methods[j].getParameterTypes()[0];
                  if ((!visitedTypes.contains(element.getClass().getName())) && type.isInstance(element)) {
                     visitedTypes.add(type.getName());
                     int paramsLength = methods[j].getParameterTypes().length;
                     Object[] params = new Object[paramsLength];
                     params[0] = element;
                     VisitorContext args = new VisitorContext(getChainConfig());
                     args.putAll(context);
                     if (paramsLength == 2) {
                        params[1] = args;
                     }
                     methods[j].invoke(visitor, params);
                     context.getVisitorMessages().addAll(args.getVisitorMessages());
                     MergeEngine me = null;
                     if (isMergeable) {
                        me = chainConfig.getConfiguration().getMergeEngine(mergePolicy);
                     }
                     if (args.hasResultNodes()) {

                        Iterator it = args.getResultNodes().iterator();

                        while (it.hasNext()) {
                           Object currentArg = it.next();
                           if (isMergeable) {
                              currentArg = merge(currentArg, me, context);
                           }

                           context.addResultNode(currentArg);

                           visit(currentArg, restVisitors, restTransformations, context);
                           return;

                        }
                     } else {
                        context.addResultNode(element);
                     }
                  }
               }
            }
            index++;
         }
      }
   }

   protected abstract Object getSourceNode(Object targetNode);

   protected void visit(Object element) throws Exception {
      VisitorContext context = new VisitorContext(getChainConfig());
      visit(element, context);
      addVisitorMessages(context);
   }

   protected void visit(Object element, VisitorContext vc) throws Exception {
      Collection colTransformations = getChainConfig().getWalkerConfig().getTransformations();
      List transformations;
      if (colTransformations instanceof List) {
         transformations = getChainConfig().getWalkerConfig().getTransformations();
      } else {
         transformations = new LinkedList(colTransformations);
      }
      visit(element, getVisitors(), transformations, vc);
      if (vc.getResultNodes() != null) {
         writeAll(vc.getResultNodes());
      }
   }

   protected void writeAll(Collection elements) throws Exception {
      if (elements != null) {
         Iterator it = elements.iterator();
         while (it.hasNext()) {
            write(it.next());
         }
      }
   }

   protected void write(Object element) throws Exception {
      write(element, null);
   }

   protected void write(Object element, VisitorContext vc) throws Exception {
      Method[] methods = writer.getClass().getMethods();
      for (int j = 0; j < methods.length; j++) {
         if (methods[j].getName().equals("write")) {
            Class type = methods[j].getParameterTypes()[0];
            int paramsLength = methods[j].getParameterTypes().length;
            Object[] params = new Object[paramsLength];
            params[0] = element;
            if (paramsLength == 2) {
               params[1] = vc;
            }
            if (type.isInstance(element)) {
               methods[j].invoke(writer, params);
            }
         }
      }
   }

   public boolean isVisitable(Object element) throws Exception {
      if (rootNamespace != null && !"".equals(rootNamespace)) {
         String qualifiedName = getResource().getNearestNamespace(element, NAMESPACE_SEPARATOR);
         if (!qualifiedName.startsWith(rootNamespace)) {
            return false;
         }
      }
      return visitedElements.add(element);
   }

   @Override
   public List getVisitors() {
      return visitor;
   }

   @Override
   public void setResource(Resource resource) {
      this.resource = resource;
   }

   public Resource getResource() {
      return resource;
   }

   public Set getVisitedElements() {
      return visitedElements;
   }

   public void setVisitedElements(Set visitedElements) {
      this.visitedElements = visitedElements;
   }

   @Override
   public void setRootNamespace(String namespace) {
      this.rootNamespace = namespace;
   }

   public String getRootNamespace() {
      return rootNamespace;
   }

   public void walk(Object element) throws Exception {
      if (element != null) {
         Collection> types = new LinkedList>();
         types.add(element.getClass());
         Queue> interfaces = new ConcurrentLinkedQueue>(types);
         Collection> visitedTypes = new LinkedList>();
         while (interfaces.size() > 0) {
            java.lang.Class type = interfaces.poll();
            if (visitedTypes.add(type)) {
               try {
                  Method m = this.getClass().getMethod("accept", type);
                  m.invoke(this, element);
                  interfaces.addAll(Arrays.asList(type.getInterfaces()));
               } catch (NoSuchMethodException e) {
               }
            }
         }
      }
   }

   @Override
   public void execute() throws Exception {
      Iterator it = getResource().iterator();
      while (it.hasNext()) {
         Object current = it.next();
         try {
            walk(current);
         } catch (WalkModException e) {
            log.error(e.getMessage());
         }
      }
   }

   @Override
   public void setWriter(Object writer) {
      this.writer = writer;
   }

   public Object getWriter() {
      return writer;
   }

   public ChainConfig getChainConfig() {
      return chainConfig;
   }

   public void setChainConfig(ChainConfig chainConfig) {
      this.chainConfig = chainConfig;
   }

   public Collection getVisitorMessages() {
      return visitorMessages;
   }

   protected void addVisitorMessages(VisitorContext ctx) {
      Collection messages = ctx.getVisitorMessages();
      String location = getLocation(ctx);
      for (String message : messages) {
         VisitorMessage m = new VisitorMessage(location, message);
         this.visitorMessages.add(m);
      }
   }

   protected abstract String getLocation(VisitorContext ctx);

   @SuppressWarnings({ "rawtypes", "unchecked" })
   protected Object merge(Object object, MergeEngine mergeEngine, VisitorContext vc) {

      Object local = null;
      Collection rnodes = vc.getResultNodes();
      boolean previousResult = false;
      Iterator it = rnodes.iterator();
      if (object instanceof IdentificableNode) {
         local = null;
         Comparator cmp = ((IdentificableNode) object).getIdentityComparator();
         boolean deleted = false;
         while (it.hasNext() && local == null) {
            Object current = it.next();
            if (current == object) {
               it.remove();
               deleted = true;
            } else if (object.getClass().equals(current.getClass())) {
               if (cmp.compare(current, object) == 0) {
                  if (deleted) {
                     local = object;
                     object = current;
                  } else {
                     local = current;
                  }
               }
            }
         }
      } else {
         it = rnodes.iterator();
         while (it.hasNext() && local == null) {
            Object current = it.next();
            if (current == object) {
               it.remove();
            } else if (current.equals(object)) {
               local = current;
            }
         }
      }
      previousResult = local != null;
      if (!previousResult) {
         local = getSourceNode(object);
      }

      if (local != null) {
         if (object instanceof Mergeable) {

            ((Mergeable) local).merge(object, mergeEngine);

            if (previousResult) {
               it.remove();
            }

            return local;

         } else {
            if (previousResult) {
               it.remove();
               // vc.addResultNode(object);
            }
            return object;
         }
      }
      if (previousResult) {
         it.remove();
      }
      return local;
   }

   @Override
   public boolean hasChanges() {
      return !(getNumModifications() == 0 && getNumAdditions() == 0 && getNumDeletions() == 0);
   }
}