package com.fasterxml.jackson.dataformat.xml.jaxb;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.*;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.XmlAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
import com.fasterxml.jackson.dataformat.xml.jaxb.XmlJaxbAnnotationIntrospector;

public class JAXBObjectId170Test extends XmlTestBase
{
    static class Company
    {
        @XmlElementWrapper(name = "computers")
        @XmlElement(name = "computer")
        public List<Computer> computers = new ArrayList<Computer>();

        @XmlElementWrapper(name = "employees")
        @XmlElement(name = "employee")
        public List<Employee> employees = new ArrayList<Employee>();

        public Company() { }

        public Company add(Computer computer) {
            if (computers == null) {
                computers = new ArrayList<Computer>();
            }
            computers.add(computer);
            return this;
        }
    }

    @XmlType(name = "employee")
    @XmlAccessorType(XmlAccessType.FIELD)
    static class Employee {
      @XmlAttribute
      @XmlID
      public String id;

      @XmlAttribute
      public String name;

      @XmlIDREF
      public Computer computer;
    }

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.WRAPPER_OBJECT,
            property = "type")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = DesktopComputer.class, name = "desktop"),
            @JsonSubTypes.Type(value = LaptopComputer.class, name = "laptop")
    })
    static class Computer {
        public String id;
    }

    static class DesktopComputer extends Computer {
        public String location;

        protected DesktopComputer() { }
        public DesktopComputer with(String id0, String l) {
            id = id0;
            location = l;
            return this;
        }
    }

    static class LaptopComputer extends Computer {
        public String vendor;
    }

    /*
    /**********************************************************
    /* Unit tests
    /**********************************************************
     */

    // for [dataformat-xml#178]
    public void testPolyIdList178() throws Exception
    {
        final String XML =
"<company>\n"+
"<computers>\n"+
"    <computers>\n"+
"      <desktop id='computer-1'>\n"+
"        <location>Bangkok</location>\n"+
"      </desktop>\n"+
"    </computers>\n"+
"    <computers>\n"+
"      <desktop id='computer-2'>\n"+
"        <location>Pattaya</location>\n"+
"      </desktop>\n"+
"    </computers>\n"+
"    <computers>\n"+
"      <laptop id='computer-3'>\n"+
"        <vendor>Apple</vendor>\n"+
"      </laptop>\n"+
"    </computers>\n"+
"  </computers>\n"+
"  <employees>\n"+
"    <employee id='emp-1' name='Robert Patrick'>\n"+
"      <computer>computer-3</computer>\n"+
"    </employee>\n"+
"    <employee id='emp-2' name='Michael Smith'>\n"+
"      <computer>computer-2</computer>\n"+
"    </employee>\n"+
"  </employees>\n"+
"</company>\n"
                ;

        XmlMapper mapper = new XmlMapper();
        XmlJaxbAnnotationIntrospector xmlIntr = new XmlJaxbAnnotationIntrospector(mapper.getTypeFactory());
        xmlIntr.setDefaultUseWrapper(false);
        AnnotationIntrospector intr = XmlAnnotationIntrospector.Pair.instance
                (xmlIntr, new JacksonAnnotationIntrospector());

        // should be default but doesn't seem to be?
        mapper.setAnnotationIntrospector(intr);

        Company result = mapper.readValue(XML, Company.class);
        assertNotNull(result);
        assertNotNull(result.employees);
        assertEquals(2, result.employees.size());
        Employee empl2 = result.employees.get(1);
        Computer comp2 = empl2.computer;
        assertEquals(DesktopComputer.class, comp2.getClass());
        assertEquals("Pattaya", ((DesktopComputer) comp2).location);
    }
}
