Menu

Specify a concrete impl for Interface/Abstrac

Dave E
2005-08-12
2013-05-02
1 2 > >> (Page 1 of 2)
  • Dave E

    Dave E - 2005-08-12

    Is there a way to 'hint' to Dozer which type of class an Interface or Abstract class may be.  I know there is a 'hint' tag but I'm guessing that this is for casting objects out of a Collection.

    I'm assuming many people will run into the problem where they'd like their objects to be defined by Interfaces and Abstract classes, yet be populated with concrete implementations of those classes.  And while I realise by defining which concrete implementation your object has in a dozerBeanMapping.xml does break the whole OO principle of Interfaces and Abstract classes, and possibly introduces some nasty runtime errors, it may be a useful feature to have.

    Eg

    public class VehicleContract
        String id
        Date date
        IVehicle vehicle

    public interface IVehicle
        getName();
        getHorsePower();

    public class LightTruck implements IVehicle
        String name;
        String horsePower

    public class PleasureBoat implements IVehicle
        String name;
        String horsePower;
        boolean hasFishFinder;

    As far as I can tell Dozer couldn't work here because IVehicle can't be instansiated.  But what if you knew that you we only going to have PleasureBoats.

    <field>
        <A concreteClass="PleasureBoat">vehicle</A>
        <B>anotherPleasureBoat</a>
    </field>

    In this case you could correctly instansiate the IVehicle to a PleaureBoat.  I guess this is similar to using the hint tag for getting/setting objects out of a Collection.

    What do you think?
    David

     
    • Franz Garsombke

      Franz Garsombke - 2005-08-12

      Hey David -

      That is a great point. You definitely bring some real world use cases to which we haven't thought up yet. We are having a roadmap/next-release meeting on Monday and are going to take this and a few other things into consideration. If you are still interested in contributing I will definitely contact you.

       
    • Ben Sullins

      Ben Sullins - 2005-08-12

      But if you have to hint the concrete class, isn't that the same as just specifying the concrete class to map?

       
    • Dave E

      Dave E - 2005-08-12

      I guess that is my question.  Can I specify a concrete class in the dozerBeanMapping.xml file so that dozer can look at my property and say

      'Well I see this is an interface or abstract class, so I can't create an instance of it, does the user give any hint to which concrete implementation should be used here'?

      In my case I'm mapping 2 models, each with rather complicated levels of object and abstractions.  One model will be reused, so it contains a lot of abstract classes for shared funtionality.

       
    • Franz Garsombke

      Franz Garsombke - 2005-08-16

      This is done and will be released today.

       
    • phantomass

      phantomass - 2005-09-15

      Hi,
      I have a similar problem.
      I want to define mapping rules between an interface(or abstract class) and an other class. The other class doesn't depend on my interface.
      eg:
      <mappings >
        <mapping>
          <classA>test.AbstractClass</classA>
          <classB>test.ASimpleClass</classB>
          <field>
            <A>name</A>
            <B>aName</B>
          </field>
        </mapping>
      </mappings>
      I am not supposed to know the name of the concret for test.AbstractClass used in runtime.

      Is there a way to solve this problem ?

       
    • Richard Lemesle

      Richard Lemesle - 2005-09-15

      Inheritance of mappings would help here ;-)

      let's take an example :

      class A {
      aA
      }

      class A1 extends A {
      aA1
      }

      class B {
      aB
      }

      class B1 extends B {
      aB1
      }

      I have the following mappings :

      <mapping>
      <classA>A</classA>
      <classB>B</classB>
      <field><A>aA</A><B>aB</B></field>
      </mapping>

      and

      <mapping>
      <classA>A1</classA>
      <classB>B1</classB>
      <field><A>aA1</A><B>aB1</B></field>
      </mapping>

      And I want the Dozer mapper to work like that :

      mapper.map(anInstanceOfA1, B.class) -> an instance of B1 correctly mapped to the instance of A1 instead of an instance of B (with only aB valued)

      With inheritance of mappings, I can define the mappings like that :

      <mapping id="mapAB">
      <classA>A</classA>
      <classB>B</classB>
      <field><A>aA</A><B>aB</B></field>
      </mapping>

      and

      <mapping id="mapA1B1" extends="mapAB" >
      <classA>A1</classA>
      <classB>B1</classB>
      <field><A>aA1</A><B>aB1</B></field>
      </mapping>

      Then, when I  map as follow :

      mapper.map(anInstanceOfA1, B.class)

      Instead of using the "mapAB" mapping, i search the "leaf" child mapping of "mapAB" between the type of anInstanceOfA1 (A1) AND a subclass of B.
      So, the mapping found is the "mapA1B1" mapping and this is the one to use...

      And with such mapping inheritance, multiple hint classes is useless ;-)

      Does it make sense ?

      Richard.

       
      • Franz Garsombke

        Franz Garsombke - 2005-09-15

        Argh. Richard, are you sure you are not Sebastian? :) The multiple hints breaks down when it is not a one-to-one relationship. I did not think about a dynamic run-time mapping of interfaces/abstract classes...since we have nothing that sophisticated. Our transfer objects are pretty flat.

        Let me take this up with the team and figure out a proper solution.

        So to answer phantomass...there is no current way to to this. You can use interfaces and abstract classes...but must provide a hint as to what object is being mapped. We need to look into inheritance of mappings to solve dynamic mapping. Once again, Seb might be correct :)

        Thanks.

        Franz

         
        • Richard Lemesle

          Richard Lemesle - 2005-09-15

          Hi again ;-)

          I'm not Seb, BUT we are working together ;-)

          He is currently in holidays for 2 weeks...

          By the way, I think that mapping inheritance idea, as proposed by Seb, was mine ;-) (Seb will correct me if I'm wrong after its holidays ;-)

          Have a good brainstorming ;-)

          Richard.

           
          • Franz Garsombke

            Franz Garsombke - 2005-09-15

            Too funny. That explains why Seb has let us off the hook for  the last week :)

            Franz

             
        • Richard Lemesle

          Richard Lemesle - 2005-09-19

          Hi Franz,

          Any news about that ?

          We want to use Dozer in some generic code and mapping inheritance would be helpful.

          For example, a part of this generic code would be able to do :

          mapper.map(aSpecificObject, GenericSupertype.class)

          and return the correct instance of the subclass of GenericSupertype (specific destination type related to the specific type of aSpecificObject) in order to pass it later to some specific code.

          Without that, Dozer can only be used in specific parts of our code because we need to specify the correct specific subclass of the map destination...

          And I'm sure there are other use cases where this would be helpful ;-)

          Richard.

           
          • Franz Garsombke

            Franz Garsombke - 2005-09-19

            I am meeting with Ben today. I will post our resolution today. If we do go the true mapping inheritance route it shouldn't take very long to implement...

            Thanks.

            Franz

             
    • Franz Garsombke

      Franz Garsombke - 2005-09-19

      So, I am still not sold on declaring mapping inheritance explicitly in our mapping files. I think Ben and I have come up with an alternate solution.

      In your example:

      mapper.map(aSpecificObject, GenericSupertype.class)

      We will search our class mappings hashmap (HashMap with ClassA/ClassB mappings) for any classes that extend/implement GenericSupertype. After finding this we will try to find a corresponding map...in this case one to aSpecificObject. This should be flexible enough for you.

      The only rule is that you can not have more than one aSpecificObject mapped to a subclass of GenericSupertype...we would have no idea which one to choose.

      Using this example the source class should be able to be an Interface, Abstract Class, or Super Class.

      mapper.map(aSpecificObject, GenericSupertype.class)

      Thoughts?

       
      • Richard Lemesle

        Richard Lemesle - 2005-09-19

        Great !

        So does the mapping found can be defined between a superclass of the class of aSpecificObject and a subclass of GenericSupertype in your example (does such case will be found by Dozer) ?

        And in case there is several mappings between aSpecificObject and subclasses of GenericSupertype (Specific1 which extends GenericSupertype and Specific2 which extends specific1 for example), is there a way to select Specific2 class ?

        I think the only case you can't select a mapping is when the mappings have differents destination types which doesn't have inheritance relationships between them (since if there is an inheritance relationship, the most concrete type need to be used)...

        Anyway, such a solution without explicit inheritance in mapping file seems ok for me ;-)

        This is really good !

        Thanks

        Richard.

         
        • Franz Garsombke

          Franz Garsombke - 2005-09-19

          >So does the mapping found can be defined between a >superclass of the class of aSpecificObject and a >subclass of GenericSupertype in your example (does >such case will be found by Dozer) ?

          Sure. Also remember that we currently aggregate all the mappings walking up and down the object graph from the source and destination class to find fields to be mapped.

          >And in case there is several mappings between >aSpecificObject and subclasses of GenericSupertype >(Specific1 which extends GenericSupertype and >Specific2 which extends specific1 for example), is there >a way to select Specific2 class ?

          Hmmmm.  I thought the goal was for aSpecificObject to be just that...a concrete implementation?

          are you saying map.(specific2, GenericSupertype.class)?

          Can you clarify this?

          Also, I just thought of something. How are you going to cast the *unknown* object type? If we are returning a subclass of GenericSuperType are you going to just cast it to GenericSuperType and then figure out what it really is later?

          franz

           
    • Richard Lemesle

      Richard Lemesle - 2005-09-19

      >Hmmmm. I thought the goal was for aSpecificObject to be just that...a concrete implementation?

      >are you saying map.(specific2, GenericSupertype.class)?

      no, I mean GenericSupertype has a concrete subclass Specific1 (having a mapping defined with a superclass of the class of aSpecificObject), BUT a MORE specific subclass Specific2 has been define extending Specific1 and this Specific2 class also have a mapping defined with a superclass of aSpecificObject.
      In this case, two mapping will be found between aSpecificObject and GenericSupertype subclasses (one with Specific1 and one with Specific2).

      But since Specific2 extends Specific1, that means that the mapping to use is the one between aSpecificObject and Specific2 ( ie map(aSpecificObject, Specific2.class) )

      I may not be clear, but it's late ;-)

      >Also, I just thought of something. How are you going to cast the *unknown* object type? If we are returning a subclass of GenericSuperType are you going to just cast it to GenericSuperType and then figure out what it really is later?

      The returned object will be cast in GenericSuperType (the only type known in our generic code) and our generic code can modify (it always using GenericSuperType). Then the generic code will call specific code (using a generic API referring GenericSuperType) with this object and the specific code will be able to cast it in the correct specific type.

      I'm sure this will be simpler to do than to explain ;-)

      Richard.

       
    • Franz Garsombke

      Franz Garsombke - 2005-09-19

      I think this will fit in nicely with what already is in place. Since we walk up and down the object graph on both ends...we should be fine if we start with the lowest subclass on the source class side and then work our way up from that...gathering all the existing mappings. Today, if we started with aSpecificObject (ASO) and Specific2 it would map from the superclass of ASO and Specific2.

      So, we just need to add functionality that walks down the object graph from the source side until it finds the lowest mapping to the destination class, or one of its super classes. We then proceed as normal really just leveraging the existing code to map from a source to a destination.

      Thoughts?

       
      • Franz Garsombke

        Franz Garsombke - 2005-09-19

        In my last post I meant Destination when I said Source and Vice-Versa.

         
        • Richard Lemesle

          Richard Lemesle - 2005-09-19

          Great !

          A good news again ;-)

          Good work !

          Richard.

           
    • Franz Garsombke

      Franz Garsombke - 2005-09-20

      Everything is coded and working. I need to create a UML diagram to explain how we walk up and down both the source and destination classes.

      One last thing I was going to add was a flag associated with each mapping which can turn on/off this functionality. The default would be 'off'. I was thinking it might get expensive to traverse the class mappings every time looking for sub classes...unless we had to.

      Thoughts?

       
    • Franz Garsombke

      Franz Garsombke - 2005-09-20

      Scratch that. I will just see if I find a mapping right away. If I don't, then I will try walking the tree.

      I will release this as 1.5.4 tomorrow. Bed time now...

       
    • Matthias Schäfer

      Hello,

      im working with version 1.5.5 for a couple of days. Yesterday we must change a class to a interface.
      This produces "net.sf.dozer.util.mapping.MappingException: java.lang.InstantiationException: BDT_Geldbetrag".

      After implementing BDT_GeldbetragCustomConverter (see below) i got now "net.sf.dozer.util.mapping.converters.ConversionException: String constructor does not exist in destination class: interface BDT_Geldbetrag"

      public interface BDT_Geldbetrag {
          public static final String DEFAULT_WAEHRUNG = "EUR";

          /**
           * @return Returns the betrag.
           */
          public BigDecimal getBetrag();

          /**
           * @param p_betrag
           *            The betrag to set.
           */
          public void setBetrag(BigDecimal p_betrag);

          /**
           * @return Returns the waehrung.
           */
          public String getWaehrung();

          /**
           * @param p_waehrung
           *            The waehrung to set.
           */
          public void setWaehrung(String p_waehrung);
      }

      public class BDT_GeldbetragCustomConverter implements CustomConverter {

          /*
           * (non-Javadoc)
           *
           * @see net.sf.dozer.util.mapping.converters.CustomConverter#convert(java.lang.Object,
           *      java.lang.Object, java.lang.Class, java.lang.Class)
           */
          public Object convert(Object destination, Object source, Class destClass, Class sourceClass) {
              BDT_GeldbetragImpl dest = null;
              //BigDecimal -> BDT_GeldbetragImpl
              if (source instanceof BigDecimal) {
                  // check to see if the object already exists
                  if (destination == null) {
                      dest = new BDT_GeldbetragImpl();
                  } else {
                      dest = (BDT_GeldbetragImpl) destination;
                  }
                  dest.setBetrag( (BigDecimal)source);
                  return dest;
              //BDT_GeldbetragImpl -> BigDecimal
              } else if (source instanceof BDT_GeldbetragImpl) {
                  return ((BDT_GeldbetragImpl) source).getBetrag();
              } else {
                  throw new MappingException(
                          "Converter BDT_GeldbetragCustomConverter used incorrectly. Arguments passed in were:"
                                  + destination + " and " + source);
              }
          }
      }

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE mappings PUBLIC "-//DOZER//DTD MAPPINGS//EN"
         "http://dozer.sourceforge.net/dtd/dozerbeanmapping.dtd">
      <mappings>
          <configuration>
              <stopOnErrors>false</stopOnErrors>
              <dateFormat>dd.MM.yyyy</dateFormat>
              <wildcard>false</wildcard>
              <customConverters> <!-- these are always bi-directional -->
               <!-- BDT_Geldbetrag -->
               <converter type="BDT_GeldbetragCustomConverter" >
                 <classA>BDT_Geldbetrag</classA>
                 <classB>java.lang.BigDecimal</classB>
               </converter>
              </customConverters>    
          </configuration>
          <mapping>
              <classA>SiO_ImmobilieImpl</classA>
              <classB>OImmobilie</classB>
              <field>
                  <!-- BDT_Geldbetrag -->
                  <A>bewertungsBetragPersonalEuroIndividuell</A>
                              <!-- java.lang.BigDecimal -->
                  <B>belhGrenzewert</B>
              </field>
          </mapping>
      </mappings>

      My Question: What went wrong with the Converter, or how can i specify a "concreteClass" ? Will the annonced "Bean Factory Injection" in 1.5.6 help me ?

      Regards

      Matthias

       
      • Franz Garsombke

        Franz Garsombke - 2005-10-28

        You can specify hints for your concrete classes:

            <field>
              <A>carMetalThingy</A> <!-- this is an interface -->
              <B>vanMetalThingy</B> <!-- this is an interface -->    <sourceTypeHint>net.sf.dozer.util.mapping.vo.Car</sourceTypeHint>
        <destinationTypeHint>net.sf.dozer.util.mapping.vo.Van</destinationTypeHint>
            </field>

        Does this help?  If you don't know the implementation until runtime then the following might help...I need to write a unit test/use case for it though...not entireley tested:

        We do support mapping interfaces.

        This documentation (at the bottom) explains how:

        http://dozer.sourceforge.net/documentation/baseattributes.html

        Basically, We walk down the abstract class, super class, or Interface until we find a subclass mapping that has a mapping to our source objects super class, or a mapping to the source object directly.

        So you need to define the mapping between the two concrete classes you plan on mapping.

        It can just be <classA>classname</a> <classB>classname</b>

        i.e. your BigDecimal and your concrete class.

         
    • Franz Garsombke

      Franz Garsombke - 2005-10-28

      I need more time to research this...we don't have a unit test with interfaces at the custom mapping level. I tried the two scenarios I listed and it still fails. Ben or Matt can you reproduce and fix this? I will be out for the next day or so...child #2 is coming :)

      Franz

       
    • Matthias Schäfer

      Hello,

      i knew the *TypeHint's. and try it again. The result is still a "net.sf.dozer.util.mapping.converters.ConversionException: String constructor does not exist in destination class: interface BDT_Geldbetrag"
      It will be ok defining the type this way before runtime. But it seems my type needs a constructor with a string for receiving the BigDecimal-Value.

      Im right ?

      Matthias

       
1 2 > >> (Page 1 of 2)

Log in to post a comment.