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

net.sf.mpxj.ikvm.MapFileGenerator Maven / Gradle / Ivy

Go to download

Library that provides facilities to allow project information to be manipulated in Java and .Net. Supports a range of data formats: Microsoft Project Exchange (MPX), Microsoft Project (MPP,MPT), Microsoft Project Data Interchange (MSPDI XML), Microsoft Project Database (MPD), Planner (XML), Primavera (PM XML, XER, and database), Asta Powerproject (PP, MDB), Asta Easyplan (PP), Phoenix Project Manager (PPX), FastTrack Schedule (FTS), and the Standard Data Exchange Format (SDEF).

There is a newer version: 13.8.0
Show newest version
/*
 * This is a modified version of the MapFileGenerator from
 * http://www.frijters.net/MapFileGenerator.java.
 *
 * The original copyright notice appears below.
 */

/*
  Copyright (C) 2005 Valdemar Mejstad
  Copyright (C) 2005 Jeroen Frijters

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*/

package net.sf.mpxj.ikvm;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

/**
 * Generate a map file for conversion of MPXJ using IKVM.
 */
public class MapFileGenerator
{
   /**
    * Command line entry point.
    *
    * @param args command line arguments
    */
   public static void main(String[] args) throws ClassNotFoundException, XMLStreamException, IOException, IntrospectionException
   {
      if (args.length != 3)
      {
         System.out.println("Usage: MapFileGenerator   ");
      }
      else
      {
         MapFileGenerator generator = new MapFileGenerator();
         generator.generateMapFile(new File(args[0]), args[1], Boolean.parseBoolean(args[2]));
      }
   }

   /**
    * Generate a map file from a jar file.
    *
    * @param jarFile jar file
    * @param mapFileName map file name
    * @param mapClassMethods true if we want to produce .Net style class method names
    */
   public void generateMapFile(File jarFile, String mapFileName, boolean mapClassMethods) throws XMLStreamException, IOException, ClassNotFoundException, IntrospectionException
   {
      writeMapFile(mapFileName, jarFile, mapClassMethods);
   }

   /**
    * Generate an IKVM map file.
    *
    * @param mapFileName map file name
    * @param jarFile jar file containing code to be mapped
    * @param mapClassMethods true if we want to produce .Net style class method names
    */
   private void writeMapFile(String mapFileName, File jarFile, boolean mapClassMethods) throws IOException, XMLStreamException, ClassNotFoundException, IntrospectionException
   {
      FileWriter fw = new FileWriter(mapFileName);
      XMLOutputFactory xof = XMLOutputFactory.newInstance();
      XMLStreamWriter writer = xof.createXMLStreamWriter(fw);
      //XMLStreamWriter writer = new IndentingXMLStreamWriter(xof.createXMLStreamWriter(fw));

      writer.writeStartDocument();
      writer.writeStartElement("root");
      writer.writeStartElement("assembly");

      addClasses(writer, jarFile, mapClassMethods);

      writer.writeEndElement();
      writer.writeEndElement();
      writer.writeEndDocument();
      writer.flush();
      writer.close();

      fw.flush();
      fw.close();
   }

   /**
    * Add classes to the map file.
    *
    * @param writer XML stream writer
    * @param jarFile jar file
    * @param mapClassMethods true if we want to produce .Net style class method names
    */
   private void addClasses(XMLStreamWriter writer, File jarFile, boolean mapClassMethods) throws IOException, ClassNotFoundException, XMLStreamException, IntrospectionException
   {
      ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();

      URLClassLoader loader = new URLClassLoader(new URL[]
      {
         jarFile.toURI().toURL()
      }, currentThreadClassLoader);

      JarFile jar = new JarFile(jarFile);
      Enumeration enumeration = jar.entries();
      while (enumeration.hasMoreElements())
      {
         JarEntry jarEntry = enumeration.nextElement();
         if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(".class"))
         {
            addClass(loader, jarEntry, writer, mapClassMethods);
         }
      }
      jar.close();
   }

   /**
    * Add an individual class to the map file.
    *
    * @param loader jar file class loader
    * @param jarEntry jar file entry
    * @param writer XML stream writer
    * @param mapClassMethods true if we want to produce .Net style class method names
    */
   private void addClass(URLClassLoader loader, JarEntry jarEntry, XMLStreamWriter writer, boolean mapClassMethods) throws ClassNotFoundException, XMLStreamException, IntrospectionException
   {
      String className = jarEntry.getName().replaceAll("\\.class", "").replaceAll("/", ".");
      writer.writeStartElement("class");
      writer.writeAttribute("name", className);

      Set methodSet = new HashSet<>();
      Class aClass = loader.loadClass(className);

      processProperties(writer, methodSet, aClass);

      if (mapClassMethods && !Modifier.isInterface(aClass.getModifiers()))
      {
         processClassMethods(writer, aClass, methodSet);
      }
      writer.writeEndElement();
   }

   /**
    * Process class properties.
    *
    * @param writer output stream
    * @param methodSet set of methods processed
    * @param aClass class being processed
    */
   private void processProperties(XMLStreamWriter writer, Set methodSet, Class aClass) throws IntrospectionException, XMLStreamException
   {
      BeanInfo beanInfo = Introspector.getBeanInfo(aClass, aClass.getSuperclass());
      PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

      for (PropertyDescriptor propertyDescriptor : propertyDescriptors)
      {
         if (propertyDescriptor.getPropertyType() != null)
         {
            String name = propertyDescriptor.getName();
            Method readMethod = propertyDescriptor.getReadMethod();
            Method writeMethod = propertyDescriptor.getWriteMethod();

            String readMethodName = readMethod == null ? null : readMethod.getName();
            String writeMethodName = writeMethod == null ? null : writeMethod.getName();
            addProperty(writer, name, propertyDescriptor.getPropertyType(), readMethodName, writeMethodName);

            if (readMethod != null)
            {
               methodSet.add(readMethod);
            }

            if (writeMethod != null)
            {
               methodSet.add(writeMethod);
            }
         }
         else
         {
            processAmbiguousProperty(writer, methodSet, aClass, propertyDescriptor);
         }
      }
   }

   /**
    * Add a simple property to the map file.
    *
    * @param writer xml stream writer
    * @param name property name
    * @param propertyType property type
    * @param readMethod read method name
    * @param writeMethod write method name
    */
   private void addProperty(XMLStreamWriter writer, String name, Class propertyType, String readMethod, String writeMethod) throws XMLStreamException
   {
      if (name.length() != 0)
      {
         writer.writeStartElement("property");

         // convert property name to .NET style (i.e. first letter uppercase)
         String propertyName = name.substring(0, 1).toUpperCase() + name.substring(1);
         writer.writeAttribute("name", propertyName);

         String type = getTypeString(propertyType);

         writer.writeAttribute("sig", "()" + type);
         if (readMethod != null)
         {
            writer.writeStartElement("getter");
            writer.writeAttribute("name", readMethod);
            writer.writeAttribute("sig", "()" + type);
            writer.writeEndElement();
         }
         if (writeMethod != null)
         {
            writer.writeStartElement("setter");
            writer.writeAttribute("name", writeMethod);
            writer.writeAttribute("sig", "(" + type + ")V");
            writer.writeEndElement();
         }

         writer.writeEndElement();
      }
   }

   /**
    * Converts a class into a signature token.
    *
    * @param c class
    * @return signature token text
    */
   private String getTypeString(Class c)
   {
      String result = TYPE_MAP.get(c);
      if (result == null)
      {
         result = c.getName();
         if (!result.endsWith(";") && !result.startsWith("["))
         {
            result = "L" + result + ";";
         }
      }
      return result;
   }

   /**
    * Where bean introspection is confused by getProperty() and getProperty(int index), this method determines the correct
    * properties to add.
    *
    * @param writer XML stream writer
    * @param methodSet set of methods processed
    * @param aClass Java class
    * @param propertyDescriptor Java property
    */
   private void processAmbiguousProperty(XMLStreamWriter writer, Set methodSet, Class aClass, PropertyDescriptor propertyDescriptor) throws SecurityException, XMLStreamException
   {
      String name = propertyDescriptor.getName();
      name = name.toUpperCase().charAt(0) + name.substring(1);

      Method readMethod = null;
      try
      {
         readMethod = aClass.getMethod("get" + name, (Class[]) null);
      }
      catch (NoSuchMethodException ex)
      {
         // Silently ignore
      }

      if (readMethod != null)
      {
         Method writeMethod = null;
         try
         {
            writeMethod = aClass.getMethod("set" + name, readMethod.getReturnType());
         }
         catch (NoSuchMethodException ex)
         {
            // Silently ignore
         }

         String readMethodName = readMethod.getName();
         String writeMethodName = writeMethod == null ? null : writeMethod.getName();
         addProperty(writer, name, readMethod.getReturnType(), readMethodName, writeMethodName);

         methodSet.add(readMethod);
         if (writeMethod != null)
         {
            methodSet.add(writeMethod);
         }
      }
   }

   /**
    * Hides the original Java-style method name using an attribute
    * which should be respected by Visual Studio, the creates a new
    * wrapper method using a .NET style method name.
    *
    * Note that this does not work for VB as it is case-insensitive. Even
    * though Visual Studio won't show you the Java-style method name,
    * the VB compiler sees both and thinks they are the same... which
    * causes it to fail.
    *
    * @param writer output stream
    * @param aClass class being processed
    * @param methodSet set of methods which have been processed.
    */
   private void processClassMethods(XMLStreamWriter writer, Class aClass, Set methodSet) throws XMLStreamException
   {
      Method[] methods = aClass.getDeclaredMethods();
      for (Method method : methods)
      {
         if (!methodSet.contains(method) && Modifier.isPublic(method.getModifiers()) && !Modifier.isInterface(method.getModifiers()))
         {
            if (Modifier.isStatic(method.getModifiers()))
            {
               // TODO Handle static methods here
            }
            else
            {
               String name = method.getName();
               String methodSignature = createMethodSignature(method);
               String fullJavaName = aClass.getCanonicalName() + "." + name + methodSignature;

               if (!ignoreMethod(fullJavaName))
               {
                  //
                  // Hide the original method
                  //
                  writer.writeStartElement("method");
                  writer.writeAttribute("name", name);
                  writer.writeAttribute("sig", methodSignature);

                  writer.writeStartElement("attribute");
                  writer.writeAttribute("type", "System.ComponentModel.EditorBrowsableAttribute");
                  writer.writeAttribute("sig", "(Lcli.System.ComponentModel.EditorBrowsableState;)V");
                  writer.writeStartElement("parameter");
                  writer.writeCharacters("Never");
                  writer.writeEndElement();
                  writer.writeEndElement();
                  writer.writeEndElement();

                  //
                  // Create a wrapper method
                  //
                  name = name.toUpperCase().charAt(0) + name.substring(1);

                  writer.writeStartElement("method");
                  writer.writeAttribute("name", name);
                  writer.writeAttribute("sig", methodSignature);
                  writer.writeAttribute("modifiers", "public");

                  writer.writeStartElement("body");

                  for (int index = 0; index <= method.getParameterTypes().length; index++)
                  {
                     if (index < 4)
                     {
                        writer.writeEmptyElement("ldarg_" + index);
                     }
                     else
                     {
                        writer.writeStartElement("ldarg_s");
                        writer.writeAttribute("argNum", Integer.toString(index));
                        writer.writeEndElement();
                     }
                  }

                  writer.writeStartElement("callvirt");
                  writer.writeAttribute("class", aClass.getName());
                  writer.writeAttribute("name", method.getName());
                  writer.writeAttribute("sig", methodSignature);
                  writer.writeEndElement();

                  if (!method.getReturnType().getName().equals("void"))
                  {
                     writer.writeEmptyElement("ldnull");
                     writer.writeEmptyElement("pop");
                  }
                  writer.writeEmptyElement("ret");
                  writer.writeEndElement();
                  writer.writeEndElement();

                  /*
                   * The private method approach doesn't work... so
                   * 3. Add EditorBrowsableAttribute (Never) to original methods
                   * 4. Generate C Sharp and VB variants of the DLL to avid case-sensitivity issues
                   * 5. Implement static method support?
                  
                  914                       Never
                  915                   
                  */
               }
            }
         }
      }
   }

   /**
    * Used to determine if the current method should be ignored.
    *
    * @param name method name
    * @return true if the method should be ignored
    */
   private boolean ignoreMethod(String name)
   {
      boolean result = false;

      for (String ignoredName : IGNORED_METHODS)
      {
         if (name.matches(ignoredName))
         {
            result = true;
            break;
         }
      }

      return result;
   }

   /**
    * Creates a method signature.
    *
    * @param method Method instance
    * @return method signature
    */
   private String createMethodSignature(Method method)
   {
      StringBuilder sb = new StringBuilder();
      sb.append("(");
      for (Class type : method.getParameterTypes())
      {
         sb.append(getTypeString(type));
      }
      sb.append(")");
      Class type = method.getReturnType();
      if (type.getName().equals("void"))
      {
         sb.append("V");
      }
      else
      {
         sb.append(getTypeString(type));
      }
      return sb.toString();
   }

   private static final Map, String> TYPE_MAP = new HashMap<>();
   static
   {
      TYPE_MAP.put(boolean.class, "Z");
      TYPE_MAP.put(byte.class, "B");
      TYPE_MAP.put(short.class, "S");
      TYPE_MAP.put(char.class, "C");
      TYPE_MAP.put(int.class, "I");
      TYPE_MAP.put(long.class, "J");
      TYPE_MAP.put(float.class, "F");
      TYPE_MAP.put(double.class, "D");
   }

   private static final String[] IGNORED_METHODS = new String[]
   {
      ".*\\.toString\\(\\)Ljava.lang.String;"
   };
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy