/*
 * Copyright 2015-2018 OpenEstate.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openestate.io.filemaker;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import org.openestate.io.core.XmlUtils;
import org.openestate.io.filemaker.xml.layout.FMPXMLLAYOUT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * XML document from Filemaker with a
 * <FMPXMLLAYOUT> root element.
 *
 * @author Andreas Rudolph
 * @since 1.0
 */
@SuppressWarnings("WeakerAccess")
public class FilemakerLayoutDocument extends FilemakerDocument<FMPXMLLAYOUT> {
    @SuppressWarnings("unused")
    private final static Logger LOGGER = LoggerFactory.getLogger(FilemakerLayoutDocument.class);

    /**
     * Create from a {@link Document}.
     *
     * @param document the document to create from
     */
    public FilemakerLayoutDocument(Document document) {
        super(document);
        if (!isReadable(document))
            throw new IllegalArgumentException("The provided document is invalid!");
    }

    /**
     * Checks, if a {@link Document} is readable as a {@link FilemakerLayoutDocument}.
     *
     * @param doc document to check
     * @return true, if the document is usable, otherwise false
     */
    public static boolean isReadable(Document doc) {
        Element root = XmlUtils.getRootElement(doc);
        return "FMPXMLLAYOUT".equals(root.getLocalName());
    }

    /**
     * Creates an empty {@link FilemakerLayoutDocument}.
     *
     * @return created document
     * @throws ParserConfigurationException if the parser is not properly configured
     * @throws JAXBException                if a problem with JAXB occurred
     */
    public static FilemakerLayoutDocument newDocument() throws ParserConfigurationException, JAXBException {
        return newDocument(FilemakerUtils.getFactoryForLayout().createFMPXMLLAYOUT());
    }

    /**
     * Creates a {@link FilemakerLayoutDocument} from a {@link FMPXMLLAYOUT} object.
     *
     * @param xmlLayout Java object, that represents the <FMPXMLLAYOUT> root element
     * @return created document
     * @throws ParserConfigurationException if the parser is not properly configured
     * @throws JAXBException                if a problem with JAXB occurred
     */
    public static FilemakerLayoutDocument newDocument(FMPXMLLAYOUT xmlLayout) throws ParserConfigurationException, JAXBException {
        Document document = XmlUtils.newDocument();
        FilemakerUtils.createMarshaller("UTF-8", true).marshal(xmlLayout, document);
        return new FilemakerLayoutDocument(document);
    }

    /**
     * Creates a {@link FMPXMLLAYOUT} object from the contained {@link Document}.
     *
     * @return created object, that represents the <FMPXMLLAYOUT> root element
     * @throws JAXBException if a problem with JAXB occurred
     */
    @Override
    public FMPXMLLAYOUT toObject() throws JAXBException {
        return (FMPXMLLAYOUT) FilemakerUtils.createUnmarshaller().unmarshal(this.getDocument());
    }
}


/*
 * Copyright 2015-2018 OpenEstate.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openestate.io.examples;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.PropertyConfigurator;
import org.openestate.io.casa_it.CasaItDocument;
import org.openestate.io.casa_it.CasaItUtils;
import org.openestate.io.casa_it.xml.Container;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

/**
 * Example for reading XML files for casa.it.
 *

 * This example illustrates how to read XML files for
 * casa.it.
 *
 * @author Andreas Rudolph
 * @since 1.0
 */
public class CasaItReadingExample {
    @SuppressWarnings("unused")
    private final static Logger LOGGER = LoggerFactory.getLogger(CasaItReadingExample.class);
    private final static String PACKAGE = "/org/openestate/io/examples";

    /**
     * Start the example application.
     *
     * @param args command line arguments
     */
    @SuppressWarnings("Duplicates")
    public static void main(String[] args) {
        // init logging
        PropertyConfigurator.configure(
                CasaItReadingExample.class.getResource(PACKAGE + "/log4j.properties"));

        // read example file, if no files were specified as command line arguments
        if (args.length < 1) {
            try {
                read(CasaItReadingExample.class.getResourceAsStream(PACKAGE + "/casa_it.xml"));
            } catch (Exception ex) {
                LOGGER.error("Can't read example file!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
                System.exit(2);
            }
        }

        // read files, that were specified as command line arguments
        else {
            for (String arg : args) {
                try {
                    read(new File(arg));
                } catch (Exception ex) {
                    LOGGER.error("Can't read file '" + arg + "'!");
                    LOGGER.error("> " + ex.getLocalizedMessage(), ex);
                    System.exit(2);
                }
            }
        }
    }

    /**
     * Read a {@link File} into a {@link CasaItDocument} and print some of its
     * content to console.
     *
     * @param xmlFile the file to read
     * @throws SAXException                 if the file is not readable by the XML parser
     * @throws IOException                  if the file is not readable
     * @throws ParserConfigurationException if the XML parser is improperly configured
     * @throws JAXBException                if XML conversion into Java objects failed
     */
    protected static void read(File xmlFile) throws SAXException, IOException, ParserConfigurationException, JAXBException {
        LOGGER.info("process file: " + xmlFile.getAbsolutePath());
        if (!xmlFile.isFile()) {
            LOGGER.warn("> provided file is invalid");
            return;
        }
        CasaItDocument doc = CasaItUtils.createDocument(xmlFile);
        if (doc == null) {
            LOGGER.warn("> provided XML is not supported");
        } else {
            printToConsole(doc);
        }
    }

    /**
     * Read an {@link InputStream} into a {@link CasaItDocument} and print some
     * of its content to console.
     *
     * @param xmlInputStream the input stream to read
     * @throws SAXException                 if the file is not readable by the XML parser
     * @throws IOException                  if the file is not readable
     * @throws ParserConfigurationException if the XML parser is improperly configured
     * @throws JAXBException                if XML conversion into Java objects failed
     */
    protected static void read(InputStream xmlInputStream) throws SAXException, IOException, ParserConfigurationException, JAXBException {
        LOGGER.info("process example file");
        CasaItDocument doc = CasaItUtils.createDocument(xmlInputStream);
        if (doc == null) {
            LOGGER.warn("> provided XML is not supported");
        } else {
            printToConsole(doc);
        }
    }

    /**
     * Print some content of a {@link CasaItDocument} to console.
     *
     * @param doc the document to process
     * @throws JAXBException if XML conversion into Java objects failed
     */
    protected static void printToConsole(CasaItDocument doc) throws JAXBException {
        Container container = doc.toObject();

        // process real estates
        if (container.getRealestateitems() != null) {
            for (Container.Realestateitems.Realestate obj : container.getRealestateitems().getRealestate()) {
                // get object nr
                String objectNr = StringUtils.trimToNull(obj.getReference());
                if (objectNr == null) objectNr = "???";

                // get object title
                String objectTitle = (obj.getDescription() != null) ?
                        StringUtils.trimToNull(obj.getDescription().getValue()) : null;
                if (objectTitle == null) objectTitle = "???";

                // print object information to console
                LOGGER.info("> found object '" + objectNr + "' "
                        + "with title '" + objectTitle + "'");
            }
        }
    }
}


package org.openestate.io.immobiliare_it.xml;

import java.io.Serializable;
import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import org.jvnet.jaxb2_commons.lang.CopyStrategy2;
import org.jvnet.jaxb2_commons.lang.CopyTo2;
import org.jvnet.jaxb2_commons.lang.Equals2;
import org.jvnet.jaxb2_commons.lang.EqualsStrategy2;
import org.jvnet.jaxb2_commons.lang.JAXBCopyStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBEqualsStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBToStringStrategy;
import org.jvnet.jaxb2_commons.lang.ToString2;
import org.jvnet.jaxb2_commons.lang.ToStringStrategy2;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;

/**
 *
Java class for boxProject complex type.
 *
 *
The following schema fragment specifies the expected content contained within this class.
 *
 *

 * <complexType name="boxProject">
 *   <simpleContent>
 *     <extension base="<http://feed.immobiliare.it>nullInteger">
 *       <attribute name="type">
 *         <simpleType>
 *           <restriction base="{http://www.w3.org/2001/XMLSchema}string">
 *             <enumeration value="No"/>
 *             <enumeration value="Box"/>
 *             <enumeration value="PostoAuto"/>
 *           </restriction>
 *         </simpleType>
 *       </attribute>
 *     </extension>
 *   </simpleContent>
 * </complexType>
 *

 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "boxProject", propOrder = {
    "value"
})
@Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
public class BoxProject implements Serializable, Cloneable, CopyTo2, Equals2, ToString2
{

    @XmlValue
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    protected String value;
    @XmlAttribute(name = "type")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    protected BoxProject.BoxProjectType type;

    /**
     * Gets the value of the value property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public String getValue() {
        return value;
    }

    /**
     * Sets the value of the value property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public void setValue(String value) {
        this.value = value;
    }

    /**
     * Gets the value of the type property.
     *
     * @return
     *     possible object is
     *     {@link BoxProject.BoxProjectType }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public BoxProject.BoxProjectType getType() {
        return type;
    }

    /**
     * Sets the value of the type property.
     *
     * @param value
     *     allowed object is
     *     {@link BoxProject.BoxProjectType }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public void setType(BoxProject.BoxProjectType value) {
        this.type = value;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public String toString() {
        final ToStringStrategy2 strategy = JAXBToStringStrategy.INSTANCE;
        final StringBuilder buffer = new StringBuilder();
        append(null, buffer, strategy);
        return buffer.toString();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder append(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        strategy.appendStart(locator, this, buffer);
        appendFields(locator, buffer, strategy);
        strategy.appendEnd(locator, this, buffer);
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder appendFields(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        {
            String theValue;
            theValue = this.getValue();
            strategy.appendField(locator, this, "value", buffer, theValue, (this.value!= null));
        }
        {
            BoxProject.BoxProjectType theType;
            theType = this.getType();
            strategy.appendField(locator, this, "type", buffer, theType, (this.type!= null));
        }
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public Object clone() {
        return copyTo(createNewInstance());
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(Object target) {
        final CopyStrategy2 strategy = JAXBCopyStrategy.INSTANCE;
        return copyTo(null, target, strategy);
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(ObjectLocator locator, Object target, CopyStrategy2 strategy) {
        final Object draftCopy = ((target == null)?createNewInstance():target);
        if (draftCopy instanceof BoxProject) {
            final BoxProject copy = ((BoxProject) draftCopy);
            {
                Boolean valueShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.value!= null));
                if (valueShouldBeCopiedAndSet == Boolean.TRUE) {
                    String sourceValue;
                    sourceValue = this.getValue();
                    String copyValue = ((String) strategy.copy(LocatorUtils.property(locator, "value", sourceValue), sourceValue, (this.value!= null)));
                    copy.setValue(copyValue);
                } else {
                    if (valueShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.value = null;
                    }
                }
            }
            {
                Boolean typeShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.type!= null));
                if (typeShouldBeCopiedAndSet == Boolean.TRUE) {
                    BoxProject.BoxProjectType sourceType;
                    sourceType = this.getType();
                    BoxProject.BoxProjectType copyType = ((BoxProject.BoxProjectType) strategy.copy(LocatorUtils.property(locator, "type", sourceType), sourceType, (this.type!= null)));
                    copy.setType(copyType);
                } else {
                    if (typeShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.type = null;
                    }
                }
            }
        }
        return draftCopy;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public Object createNewInstance() {
        return new BoxProject();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) {
        if ((object == null)||(this.getClass()!= object.getClass())) {
            return false;
        }
        if (this == object) {
            return true;
        }
        final BoxProject that = ((BoxProject) object);
        {
            String lhsValue;
            lhsValue = this.getValue();
            String rhsValue;
            rhsValue = that.getValue();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "value", lhsValue), LocatorUtils.property(thatLocator, "value", rhsValue), lhsValue, rhsValue, (this.value!= null), (that.value!= null))) {
                return false;
            }
        }
        {
            BoxProject.BoxProjectType lhsType;
            lhsType = this.getType();
            BoxProject.BoxProjectType rhsType;
            rhsType = that.getType();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "type", lhsType), LocatorUtils.property(thatLocator, "type", rhsType), lhsType, rhsType, (this.type!= null), (that.type!= null))) {
                return false;
            }
        }
        return true;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(Object object) {
        final EqualsStrategy2 strategy = JAXBEqualsStrategy.INSTANCE;
        return equals(null, null, object, strategy);
    }

    /**
     *
Java class for null.
     *
     *
The following schema fragment specifies the expected content contained within this class.
     *

     *

     * <simpleType>
     *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
     *     <enumeration value="No"/>
     *     <enumeration value="Box"/>
     *     <enumeration value="PostoAuto"/>
     *   </restriction>
     * </simpleType>
     *

     *
     */
    @XmlType(name = "")
    @XmlEnum
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:48:12+02:00", comments = "JAXB RI v2.2.11")
    public enum BoxProjectType {

        @XmlEnumValue("No")
        NO("No"),
        @XmlEnumValue("Box")
        BOX("Box"),
        @XmlEnumValue("PostoAuto")
        POSTO_AUTO("PostoAuto");
        private final String value;

        BoxProjectType(String v) {
            value = v;
        }

        public String value() {
            return value;
        }

        public static BoxProject.BoxProjectType fromValue(String v) {
            for (BoxProject.BoxProjectType c: BoxProject.BoxProjectType.values()) {
                if (c.value.equals(v)) {
                    return c;
                }
            }
            throw new IllegalArgumentException(v);
        }

    }

}


package org.openestate.io.immoxml.xml;

import java.io.Serializable;
import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.jvnet.jaxb2_commons.lang.CopyStrategy2;
import org.jvnet.jaxb2_commons.lang.CopyTo2;
import org.jvnet.jaxb2_commons.lang.Equals2;
import org.jvnet.jaxb2_commons.lang.EqualsStrategy2;
import org.jvnet.jaxb2_commons.lang.JAXBCopyStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBEqualsStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBToStringStrategy;
import org.jvnet.jaxb2_commons.lang.ToString2;
import org.jvnet.jaxb2_commons.lang.ToStringStrategy2;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;

/**
 * Java class for <bebaubar_nach> element.
 *
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "bebaubar_nach")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
public class BebaubarNach implements Serializable, Cloneable, CopyTo2, Equals2, ToString2
{

    @XmlAttribute(name = "bebaubar_attr")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected BebaubarNach.BebaubarAttr bebaubarAttr;

    /**
     * Gets the value of the bebaubarAttr property.
     *
     * @return
     *     possible object is
     *     {@link BebaubarNach.BebaubarAttr }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public BebaubarNach.BebaubarAttr getBebaubarAttr() {
        return bebaubarAttr;
    }

    /**
     * Sets the value of the bebaubarAttr property.
     *
     * @param value
     *     allowed object is
     *     {@link BebaubarNach.BebaubarAttr }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public void setBebaubarAttr(BebaubarNach.BebaubarAttr value) {
        this.bebaubarAttr = value;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public String toString() {
        final ToStringStrategy2 strategy = JAXBToStringStrategy.INSTANCE;
        final StringBuilder buffer = new StringBuilder();
        append(null, buffer, strategy);
        return buffer.toString();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder append(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        strategy.appendStart(locator, this, buffer);
        appendFields(locator, buffer, strategy);
        strategy.appendEnd(locator, this, buffer);
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder appendFields(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        {
            BebaubarNach.BebaubarAttr theBebaubarAttr;
            theBebaubarAttr = this.getBebaubarAttr();
            strategy.appendField(locator, this, "bebaubarAttr", buffer, theBebaubarAttr, (this.bebaubarAttr!= null));
        }
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public Object clone() {
        return copyTo(createNewInstance());
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(Object target) {
        final CopyStrategy2 strategy = JAXBCopyStrategy.INSTANCE;
        return copyTo(null, target, strategy);
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(ObjectLocator locator, Object target, CopyStrategy2 strategy) {
        final Object draftCopy = ((target == null)?createNewInstance():target);
        if (draftCopy instanceof BebaubarNach) {
            final BebaubarNach copy = ((BebaubarNach) draftCopy);
            {
                Boolean bebaubarAttrShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.bebaubarAttr!= null));
                if (bebaubarAttrShouldBeCopiedAndSet == Boolean.TRUE) {
                    BebaubarNach.BebaubarAttr sourceBebaubarAttr;
                    sourceBebaubarAttr = this.getBebaubarAttr();
                    BebaubarNach.BebaubarAttr copyBebaubarAttr = ((BebaubarNach.BebaubarAttr) strategy.copy(LocatorUtils.property(locator, "bebaubarAttr", sourceBebaubarAttr), sourceBebaubarAttr, (this.bebaubarAttr!= null)));
                    copy.setBebaubarAttr(copyBebaubarAttr);
                } else {
                    if (bebaubarAttrShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.bebaubarAttr = null;
                    }
                }
            }
        }
        return draftCopy;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public Object createNewInstance() {
        return new BebaubarNach();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) {
        if ((object == null)||(this.getClass()!= object.getClass())) {
            return false;
        }
        if (this == object) {
            return true;
        }
        final BebaubarNach that = ((BebaubarNach) object);
        {
            BebaubarNach.BebaubarAttr lhsBebaubarAttr;
            lhsBebaubarAttr = this.getBebaubarAttr();
            BebaubarNach.BebaubarAttr rhsBebaubarAttr;
            rhsBebaubarAttr = that.getBebaubarAttr();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "bebaubarAttr", lhsBebaubarAttr), LocatorUtils.property(thatLocator, "bebaubarAttr", rhsBebaubarAttr), lhsBebaubarAttr, rhsBebaubarAttr, (this.bebaubarAttr!= null), (that.bebaubarAttr!= null))) {
                return false;
            }
        }
        return true;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(Object object) {
        final EqualsStrategy2 strategy = JAXBEqualsStrategy.INSTANCE;
        return equals(null, null, object, strategy);
    }

    /**
     *
     *                     Java enum for bebaubar_attr attribute in <bebaubar_nach> elements.
     *
     *
Java class for null.
     *
     *
The following schema fragment specifies the expected content contained within this class.
     *

     *

     * <simpleType>
     *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
     *     <enumeration value="34_NACHBARSCHAFT"/>
     *     <enumeration value="35_AUSSENGEBIET"/>
     *     <enumeration value="B_PLAN"/>
     *     <enumeration value="KEIN BAULAND"/>
     *     <enumeration value="BAUERWARTUNGSLAND"/>
     *   </restriction>
     * </simpleType>
     *

     *
     */
    @XmlType(name = "")
    @XmlEnum
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public enum BebaubarAttr {

        @XmlEnumValue("34_NACHBARSCHAFT")
        NACHBARSCHAFT("34_NACHBARSCHAFT"),
        @XmlEnumValue("35_AUSSENGEBIET")
        AUSSENGEBIET("35_AUSSENGEBIET"),
        B_PLAN("B_PLAN"),
        @XmlEnumValue("KEIN BAULAND")
        KEIN_BAULAND("KEIN BAULAND"),
        BAUERWARTUNGSLAND("BAUERWARTUNGSLAND");
        private final String value;

        BebaubarAttr(String v) {
            value = v;
        }

        public String value() {
            return value;
        }

        public static BebaubarNach.BebaubarAttr fromValue(String v) {
            for (BebaubarNach.BebaubarAttr c: BebaubarNach.BebaubarAttr.values()) {
                if (c.value.equals(v)) {
                    return c;
                }
            }
            throw new IllegalArgumentException(v);
        }

    }

}


/*
 * Copyright 2015-2018 OpenEstate.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openestate.io.openimmo.converters;

import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.jaxen.JaxenException;
import org.openestate.io.core.XmlConverter;
import org.openestate.io.core.XmlUtils;
import org.openestate.io.openimmo.OpenImmoDocument;
import org.openestate.io.openimmo.OpenImmoFeedbackDocument;
import org.openestate.io.openimmo.OpenImmoTransferDocument;
import org.openestate.io.openimmo.OpenImmoVersion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Converter for version 1.2.4.
 *
 * @author Andreas Rudolph
 * @since 1.0
 */
@SuppressWarnings({"SpellCheckingInspection", "WeakerAccess"})
public class OpenImmo_1_2_4 extends XmlConverter<OpenImmoDocument, OpenImmoVersion> {
    @SuppressWarnings("unused")
    private final static Logger LOGGER = LoggerFactory.getLogger(OpenImmo_1_2_4.class);

    @Override
    public OpenImmoVersion getVersion() {
        return OpenImmoVersion.V1_2_4;
    }

    /**
     * Downgrade an OpenImmo document from version 1.2.4 to 1.2.3.
     *
     * @param doc OpenImmo document in version 1.2.4
     */
    @Override
    public void downgradeToPreviousVersion(OpenImmoDocument doc) {
        doc.setDocumentVersion(OpenImmoVersion.V1_2_3);

        // downgrade a feedback document
        if (doc instanceof OpenImmoFeedbackDocument) {
            try {
                this.removeFeedbackVersionElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't remove unsupported <version> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.removeFeedbackInteressentChildElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't remove unsupported children of <interessent> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }
        }

        // downgrade a transfer document
        else if (doc instanceof OpenImmoTransferDocument) {
            try {
                this.removePreiseChildElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't remove unsupported children of <preise> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.removeWintergartenElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't remove unsupported <wintergarten> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.downgradeEnergietypElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't downgrade <energietyp> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.downgradeBebaubarNachElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't downgrade <bebaubar_nach> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.downgradeAnhangElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't downgrade <anhang> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.downgradeWohnungElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't downgrade <wohnung> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.downgradeGrundstueckElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't downgrade <grundstueck> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.downgradeLandUndForstwirtschaftElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't downgrade <land_und_forstwirtschaft> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.downgradeParkenElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't downgrade <parken> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }
        }
    }

    /**
     * Upgrade an OpenImmo document from version 1.2.3 to 1.2.4.
     *
     * @param doc OpenImmo document in version 1.2.3
     */
    @Override
    public void upgradeFromPreviousVersion(OpenImmoDocument doc) {
        doc.setDocumentVersion(OpenImmoVersion.V1_2_4);

        if (doc instanceof OpenImmoTransferDocument) {
            try {
                this.upgradeAnzahlBalkonTerrassenElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't upgrade <anzahl_balkon_terrassen> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.upgradeAnhangElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't upgrade <anhang> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }

            try {
                this.upgradeSonstigeElements(doc.getDocument());
            } catch (Exception ex) {
                LOGGER.error("Can't upgrade <sonstige> elements!");
                LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            }
        }
    }

    /**
     * Downgrade <anhang> elements to OpenImmo 1.2.3.
     *

     * The options "QRCODE", "FILM", "FILMLINK" for the "gruppe" attribute of
     * <anhang> elements are not available in version 1.2.3.
     *

     * The option "REMOTE" for the "location" attribute of
     * <anhang> elements is not available in version 1.2.3.
     *

     * The the child element <check> of <anhang> elements is not
     * available in version 1.2.3.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void downgradeAnhangElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:anhang | " +
                        "/io:openimmo/io:anbieter/io:immobilie/io:anhaenge/io:anhang",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;

            String value = StringUtils.trimToNull(node.getAttribute("gruppe"));
            if ("QRCODE".equalsIgnoreCase(value))
                node.removeAttribute("gruppe");
            else if ("FILM".equalsIgnoreCase(value))
                node.removeAttribute("gruppe");
            else if ("FILMLINK".equalsIgnoreCase(value))
                node.removeAttribute("gruppe");

            value = StringUtils.trimToNull(node.getAttribute("location"));
            if ("REMOTE".equalsIgnoreCase(value))
                node.setAttribute("location", "EXTERN");

            List childNodes = XmlUtils.newXPath("io:check", doc)
                    .selectNodes(node);
            for (Object childItem : childNodes) {
                node.removeChild((Node) childItem);
            }
        }
    }

    /**
     * Downgrade <bebaubar_nach> elements to OpenImmo 1.2.3.
     *

     * The option "LAENDERSPEZIFISCH" for the "bebaubar_attr" attribute of
     * <bebaubar_nach> elements is not available in version 1.2.3.
     *

     * Any occurences of these values are removed.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void downgradeBebaubarNachElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:zustand_angaben/io:bebaubar_nach[@bebaubar_attr]",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            String value = StringUtils.trimToNull(node.getAttribute("bebaubar_attr"));
            if ("LAENDERSPEZIFISCH".equalsIgnoreCase(value))
                node.removeAttribute("bebaubar_attr");
        }
    }

    /**
     * Downgrade <energietyp> elements to OpenImmo 1.2.3.
     *

     * The attributes "KFW55", "KFW70" of <energietyp> elements are not
     * available in OpenImmo 1.2.3.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void downgradeEnergietypElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:ausstattung/io:energietyp[@KFW55 or @KFW70]",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            node.removeAttribute("KFW55");
            node.removeAttribute("KFW70");
        }
    }

    /**
     * Downgrade <grundstueck> elements to OpenImmo 1.2.3.
     *

     * The option "SEELIEGENSCHAFT" for the "grundst_typ" attribute of
     * <grundstueck> elements is not available in version 1.2.3.
     *

     * Any occurence of these values is removed.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void downgradeGrundstueckElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:objektkategorie/io:objektart/io:grundstueck[@grundst_typ]",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            String value = StringUtils.trimToNull(node.getAttribute("grundst_typ"));
            if ("SEELIEGENSCHAFT".equalsIgnoreCase(value))
                node.removeAttribute("grundst_typ");
        }
    }

    /**
     * Downgrade <land_und_forstwirtschaft> elements to OpenImmo 1.2.3.
     *

     * The option "JAGDREVIER" for the "land_typ" attribute of
     * <land_und_forstwirtschaft> elements is not available in version
     * 1.2.3.
     *

     * Any occurence of these values is replaced by the
     * "SONSTIGE_LANDWIRTSCHAFTSIMMOBILIEN" value.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void downgradeLandUndForstwirtschaftElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:objektkategorie/io:objektart/io:land_und_forstwirtschaft[@land_typ]",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            String value = StringUtils.trimToNull(node.getAttribute("land_typ"));
            if ("JAGDREVIER".equalsIgnoreCase(value))
                node.setAttribute("land_typ", "SONSTIGE_LANDWIRTSCHAFTSIMMOBILIEN");
        }
    }

    /**
     * Downgrade <parken> elements to OpenImmo 1.2.3.
     *

     * The options "EINZELGARAGE", "PARKHAUS", "TIEFGARAGENSTELLPLATZ",
     * "PARKPLATZ_STROM" for the "parken_typ" attribute of
     * <parken> elements are not available in version 1.2.3.
     *

     * Any occurence of these values is replaced by the "STELLPLATZ" value.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void downgradeParkenElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:objektkategorie/io:objektart/io:parken[@parken_typ]",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            String value = StringUtils.trimToNull(node.getAttribute("parken_typ"));
            if ("EINZELGARAGE".equalsIgnoreCase(value))
                node.setAttribute("parken_typ", "STELLPLATZ");
            else if ("PARKHAUS".equalsIgnoreCase(value))
                node.setAttribute("parken_typ", "STELLPLATZ");
            else if ("TIEFGARAGENSTELLPLATZ".equalsIgnoreCase(value))
                node.setAttribute("parken_typ", "STELLPLATZ");
            else if ("PARKPLATZ_STROM".equalsIgnoreCase(value))
                node.setAttribute("parken_typ", "STELLPLATZ");
        }
    }

    /**
     * Downgrade <wohnung> elements to OpenImmo 1.2.3.
     *

     * The option "ROHDACHBODEN" for the "wohnungtyp" attribute of
     * <wohnung> elements is not available in version 1.2.3.
     *

     * Any occurence of these values is replaced by the "KEINE_ANGABE" value.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void downgradeWohnungElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:objektkategorie/io:objektart/io:wohnung[@wohnungtyp]",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            String value = StringUtils.trimToNull(node.getAttribute("wohnungtyp"));
            if ("ROHDACHBODEN".equalsIgnoreCase(value))
                node.setAttribute("wohnungtyp", "KEINE_ANGABE");
        }
    }

    /**
     * Remove unsupported children from all <interessent> elements in
     * feedback XML.
     *

     * OpenImmo 1.2.3 does not support more then one <bevorzugt>,
     * <wunsch> elements in feedback XML.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    @SuppressWarnings("Duplicates")
    protected void removeFeedbackInteressentChildElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo_feedback/io:objekt/io:interessent",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element parentNode = (Element) item;
            boolean bevorzugtPassed = false;
            boolean wunschPassed = false;

            List childNodes = XmlUtils.newXPath(
                    "io:bevorzugt", doc).selectNodes(parentNode);
            for (Object childItem : childNodes) {
                Element node = (Element) childItem;
                if (!bevorzugtPassed) bevorzugtPassed = true;
                else parentNode.removeChild(node);
            }

            childNodes = XmlUtils.newXPath(
                    "io:wunsch", doc).selectNodes(parentNode);
            for (Object childItem : childNodes) {
                Element node = (Element) childItem;
                if (!wunschPassed) wunschPassed = true;
                else parentNode.removeChild(node);
            }
        }
    }

    /**
     * Remove <version> elements in feedback XML.
     *

     * OpenImmo 1.2.3 does not support <version> elements in feedback XML.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void removeFeedbackVersionElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo_feedback/io:version",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            Element parentNode = (Element) node.getParentNode();
            parentNode.removeChild(node);
        }
    }

    /**
     * Remove unsupported children from all <preise> elements.
     *

     * OpenImmo 1.2.3 does not support the following children for
     * <preise> elements: <provision_teilen>, <kaution_text>,
     * <richtpreis>
     *

     * These elements are removed by this function.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void removePreiseChildElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:preise/io:provision_teilen | " +
                        "/io:openimmo/io:anbieter/io:immobilie/io:preise/io:kaution_text | " +
                        "/io:openimmo/io:anbieter/io:immobilie/io:preise/io:richtpreis",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            Element parentNode = (Element) node.getParentNode();
            parentNode.removeChild(node);
        }
    }

    /**
     * Remove <wintergarten> elements.
     *

     * OpenImmo 1.2.3 does not support <wintergarten> elements.
     *
     * @param doc OpenImmo document in version 1.2.4
     * @throws JaxenException if xpath evaluation failed
     */
    protected void removeWintergartenElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:ausstattung/io:wintergarten",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            Element parentNode = (Element) node.getParentNode();
            parentNode.removeChild(node);
        }
    }

    /**
     * Upgrade <anhang> elements to OpenImmo 1.2.4.
     *

     * The option "REMOTE" for the "location" attribute of <anhang> elements
     * is introduced with OpenImmo 1.2.4.
     *

     * If the <pfad> element of an <anhang> element contains an URL
     * (beginning with "http://" / "https://" / "ftp://" / "ftps://"), the value
     * of the "location" attribute is changed to "REMOTE".
     *
     * @param doc OpenImmo document in version 1.2.3
     * @throws JaxenException if xpath evaluation failed
     */
    protected void upgradeAnhangElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:anhang/io:daten/io:pfad | " +
                        "/io:openimmo/io:anbieter/io:immobilie/io:anhaenge/io:anhang/io:daten/io:pfad",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            Element parentNode = (Element) node.getParentNode().getParentNode();
            String value = StringUtils.trimToEmpty(node.getTextContent()).toLowerCase();
            if (value.startsWith("http://"))
                parentNode.setAttribute("location", "REMOTE");
            else if (value.startsWith("https://"))
                parentNode.setAttribute("location", "REMOTE");
            else if (value.startsWith("ftp://"))
                parentNode.setAttribute("location", "REMOTE");
            else if (value.startsWith("ftps://"))
                parentNode.setAttribute("location", "REMOTE");
        }
    }

    /**
     * Upgrade <anzahl_balkon_terrassen> elements to OpenImmo 1.2.4.
     *

     * The <anzahl_balkon_terrassen> is not supported anymore in version
     * 1.2.4. The element is replaced by <anzahl_balkone> and
     * <anzahl_terrassen>.
     *

     * Any <anzahl_balkon_terrassen> element is removed. Its content is
     * copied into <anzahl_balkone>, if this element is not already present.
     *
     * @param doc OpenImmo document in version 1.2.3
     * @throws JaxenException if xpath evaluation failed
     */
    protected void upgradeAnzahlBalkonTerrassenElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:flaechen/io:anzahl_balkon_terrassen",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            Element parentNode = (Element) node.getParentNode();
            String value = StringUtils.trimToNull(node.getTextContent());
            if (value != null) {
                Element newNode = (Element) XmlUtils.newXPath(
                        "io:anzahl_balkone", doc).selectSingleNode(parentNode);
                if (newNode == null) {
                    newNode = doc.createElementNS(StringUtils.EMPTY, "anzahl_balkone");
                    newNode.setTextContent(value);
                    parentNode.appendChild(newNode);
                } else if (StringUtils.isBlank(newNode.getTextContent())) {
                    newNode.setTextContent(value);
                }
            }
            parentNode.removeChild(node);
        }
    }

    /**
     * Upgrade <sonstige> elements to OpenImmo 1.2.4.
     *

     * The options "GARAGEN", "PARKFLACHE" for the "sonstige_typ" attribute of
     * <sonstige> elements were removed with OpenImmo 1.2.4.
     *

     * For any occurence of these values the corresponding <sonstige>
     * element is replaced with a <parken> element.
     *
     * @param doc OpenImmo document in version 1.2.3
     * @throws JaxenException if xpath evaluation failed
     */
    protected void upgradeSonstigeElements(Document doc) throws JaxenException {
        List nodes = XmlUtils.newXPath(
                "/io:openimmo/io:anbieter/io:immobilie/io:objektkategorie/io:objektart/io:sonstige[@sonstige_typ]",
                doc).selectNodes(doc);
        for (Object item : nodes) {
            Element node = (Element) item;
            String value = StringUtils.trimToNull(node.getAttribute("sonstige_typ"));
            if ("GARAGEN".equalsIgnoreCase(value) || "PARKFLACHE".equalsIgnoreCase(value)) {
                Element parentNode = (Element) node.getParentNode();
                parentNode.removeChild(node);

                Element newNode = doc.createElementNS(StringUtils.EMPTY, "parken");
                newNode.setAttribute("parken_typ", "STELLPLATZ");
                parentNode.appendChild(newNode);
            }
        }
    }
}


package org.openestate.io.openimmo.xml;

import java.io.Serializable;
import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.jvnet.jaxb2_commons.lang.CopyStrategy2;
import org.jvnet.jaxb2_commons.lang.CopyTo2;
import org.jvnet.jaxb2_commons.lang.Equals2;
import org.jvnet.jaxb2_commons.lang.EqualsStrategy2;
import org.jvnet.jaxb2_commons.lang.JAXBCopyStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBEqualsStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBToStringStrategy;
import org.jvnet.jaxb2_commons.lang.ToString2;
import org.jvnet.jaxb2_commons.lang.ToStringStrategy2;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;

/**
 * Java class for <parken> element.
 *
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "parken")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
public class Parken implements Serializable, Cloneable, CopyTo2, Equals2, ToString2
{

    @XmlAttribute(name = "parken_typ")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    protected Parken.ParkenTyp parkenTyp;

    /**
     * Gets the value of the parkenTyp property.
     *
     * @return
     *     possible object is
     *     {@link Parken.ParkenTyp }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Parken.ParkenTyp getParkenTyp() {
        return parkenTyp;
    }

    /**
     * Sets the value of the parkenTyp property.
     *
     * @param value
     *     allowed object is
     *     {@link Parken.ParkenTyp }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public void setParkenTyp(Parken.ParkenTyp value) {
        this.parkenTyp = value;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public String toString() {
        final ToStringStrategy2 strategy = JAXBToStringStrategy.INSTANCE;
        final StringBuilder buffer = new StringBuilder();
        append(null, buffer, strategy);
        return buffer.toString();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder append(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        strategy.appendStart(locator, this, buffer);
        appendFields(locator, buffer, strategy);
        strategy.appendEnd(locator, this, buffer);
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder appendFields(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        {
            Parken.ParkenTyp theParkenTyp;
            theParkenTyp = this.getParkenTyp();
            strategy.appendField(locator, this, "parkenTyp", buffer, theParkenTyp, (this.parkenTyp!= null));
        }
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object clone() {
        return copyTo(createNewInstance());
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(Object target) {
        final CopyStrategy2 strategy = JAXBCopyStrategy.INSTANCE;
        return copyTo(null, target, strategy);
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(ObjectLocator locator, Object target, CopyStrategy2 strategy) {
        final Object draftCopy = ((target == null)?createNewInstance():target);
        if (draftCopy instanceof Parken) {
            final Parken copy = ((Parken) draftCopy);
            {
                Boolean parkenTypShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.parkenTyp!= null));
                if (parkenTypShouldBeCopiedAndSet == Boolean.TRUE) {
                    Parken.ParkenTyp sourceParkenTyp;
                    sourceParkenTyp = this.getParkenTyp();
                    Parken.ParkenTyp copyParkenTyp = ((Parken.ParkenTyp) strategy.copy(LocatorUtils.property(locator, "parkenTyp", sourceParkenTyp), sourceParkenTyp, (this.parkenTyp!= null)));
                    copy.setParkenTyp(copyParkenTyp);
                } else {
                    if (parkenTypShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.parkenTyp = null;
                    }
                }
            }
        }
        return draftCopy;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object createNewInstance() {
        return new Parken();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) {
        if ((object == null)||(this.getClass()!= object.getClass())) {
            return false;
        }
        if (this == object) {
            return true;
        }
        final Parken that = ((Parken) object);
        {
            Parken.ParkenTyp lhsParkenTyp;
            lhsParkenTyp = this.getParkenTyp();
            Parken.ParkenTyp rhsParkenTyp;
            rhsParkenTyp = that.getParkenTyp();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "parkenTyp", lhsParkenTyp), LocatorUtils.property(thatLocator, "parkenTyp", rhsParkenTyp), lhsParkenTyp, rhsParkenTyp, (this.parkenTyp!= null), (that.parkenTyp!= null))) {
                return false;
            }
        }
        return true;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(Object object) {
        final EqualsStrategy2 strategy = JAXBEqualsStrategy.INSTANCE;
        return equals(null, null, object, strategy);
    }

    /**
     *
     *                     Java enum for parken_typ attribute in <parken> elements.
     *
     *
Java class for null.
     *
     *
The following schema fragment specifies the expected content contained within this class.
     *

     *

     * <simpleType>
     *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
     *     <enumeration value="STELLPLATZ"/>
     *     <enumeration value="CARPORT"/>
     *     <enumeration value="DOPPELGARAGE"/>
     *     <enumeration value="DUPLEX"/>
     *     <enumeration value="TIEFGARAGE"/>
     *     <enumeration value="BOOTSLIEGEPLATZ"/>
     *     <enumeration value="EINZELGARAGE"/>
     *     <enumeration value="PARKHAUS"/>
     *     <enumeration value="TIEFGARAGENSTELLPLATZ"/>
     *     <enumeration value="PARKPLATZ_STROM"/>
     *   </restriction>
     * </simpleType>
     *

     *
     */
    @XmlType(name = "")
    @XmlEnum
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public enum ParkenTyp {

        STELLPLATZ,
        CARPORT,
        DOPPELGARAGE,
        DUPLEX,
        TIEFGARAGE,
        BOOTSLIEGEPLATZ,
        EINZELGARAGE,
        PARKHAUS,
        TIEFGARAGENSTELLPLATZ,
        PARKPLATZ_STROM;

        public String value() {
            return name();
        }

        public static Parken.ParkenTyp fromValue(String v) {
            return valueOf(v);
        }

    }

}


package org.openestate.io.openimmo.xml;

import java.io.Serializable;
import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.jvnet.jaxb2_commons.lang.CopyStrategy2;
import org.jvnet.jaxb2_commons.lang.CopyTo2;
import org.jvnet.jaxb2_commons.lang.Equals2;
import org.jvnet.jaxb2_commons.lang.EqualsStrategy2;
import org.jvnet.jaxb2_commons.lang.JAXBCopyStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBEqualsStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBToStringStrategy;
import org.jvnet.jaxb2_commons.lang.ToString2;
import org.jvnet.jaxb2_commons.lang.ToStringStrategy2;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;

/**
 * Java class for <foto> element.
 *
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "format",
    "daten"
})
@XmlRootElement(name = "foto")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
public class Foto implements Serializable, Cloneable, CopyTo2, Equals2, ToString2
{

    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    protected String format;
    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    protected Daten daten;
    @XmlAttribute(name = "location", required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    protected Foto.Location location;

    /**
     * Gets the value of the format property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public String getFormat() {
        return format;
    }

    /**
     * Sets the value of the format property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public void setFormat(String value) {
        this.format = value;
    }

    /**
     * Gets the value of the daten property.
     *
     * @return
     *     possible object is
     *     {@link Daten }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Daten getDaten() {
        return daten;
    }

    /**
     * Sets the value of the daten property.
     *
     * @param value
     *     allowed object is
     *     {@link Daten }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public void setDaten(Daten value) {
        this.daten = value;
    }

    /**
     * Gets the value of the location property.
     *
     * @return
     *     possible object is
     *     {@link Foto.Location }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Foto.Location getLocation() {
        return location;
    }

    /**
     * Sets the value of the location property.
     *
     * @param value
     *     allowed object is
     *     {@link Foto.Location }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public void setLocation(Foto.Location value) {
        this.location = value;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public String toString() {
        final ToStringStrategy2 strategy = JAXBToStringStrategy.INSTANCE;
        final StringBuilder buffer = new StringBuilder();
        append(null, buffer, strategy);
        return buffer.toString();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder append(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        strategy.appendStart(locator, this, buffer);
        appendFields(locator, buffer, strategy);
        strategy.appendEnd(locator, this, buffer);
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder appendFields(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        {
            String theFormat;
            theFormat = this.getFormat();
            strategy.appendField(locator, this, "format", buffer, theFormat, (this.format!= null));
        }
        {
            Daten theDaten;
            theDaten = this.getDaten();
            strategy.appendField(locator, this, "daten", buffer, theDaten, (this.daten!= null));
        }
        {
            Foto.Location theLocation;
            theLocation = this.getLocation();
            strategy.appendField(locator, this, "location", buffer, theLocation, (this.location!= null));
        }
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object clone() {
        return copyTo(createNewInstance());
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(Object target) {
        final CopyStrategy2 strategy = JAXBCopyStrategy.INSTANCE;
        return copyTo(null, target, strategy);
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(ObjectLocator locator, Object target, CopyStrategy2 strategy) {
        final Object draftCopy = ((target == null)?createNewInstance():target);
        if (draftCopy instanceof Foto) {
            final Foto copy = ((Foto) draftCopy);
            {
                Boolean formatShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.format!= null));
                if (formatShouldBeCopiedAndSet == Boolean.TRUE) {
                    String sourceFormat;
                    sourceFormat = this.getFormat();
                    String copyFormat = ((String) strategy.copy(LocatorUtils.property(locator, "format", sourceFormat), sourceFormat, (this.format!= null)));
                    copy.setFormat(copyFormat);
                } else {
                    if (formatShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.format = null;
                    }
                }
            }
            {
                Boolean datenShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.daten!= null));
                if (datenShouldBeCopiedAndSet == Boolean.TRUE) {
                    Daten sourceDaten;
                    sourceDaten = this.getDaten();
                    Daten copyDaten = ((Daten) strategy.copy(LocatorUtils.property(locator, "daten", sourceDaten), sourceDaten, (this.daten!= null)));
                    copy.setDaten(copyDaten);
                } else {
                    if (datenShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.daten = null;
                    }
                }
            }
            {
                Boolean locationShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.location!= null));
                if (locationShouldBeCopiedAndSet == Boolean.TRUE) {
                    Foto.Location sourceLocation;
                    sourceLocation = this.getLocation();
                    Foto.Location copyLocation = ((Foto.Location) strategy.copy(LocatorUtils.property(locator, "location", sourceLocation), sourceLocation, (this.location!= null)));
                    copy.setLocation(copyLocation);
                } else {
                    if (locationShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.location = null;
                    }
                }
            }
        }
        return draftCopy;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object createNewInstance() {
        return new Foto();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) {
        if ((object == null)||(this.getClass()!= object.getClass())) {
            return false;
        }
        if (this == object) {
            return true;
        }
        final Foto that = ((Foto) object);
        {
            String lhsFormat;
            lhsFormat = this.getFormat();
            String rhsFormat;
            rhsFormat = that.getFormat();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "format", lhsFormat), LocatorUtils.property(thatLocator, "format", rhsFormat), lhsFormat, rhsFormat, (this.format!= null), (that.format!= null))) {
                return false;
            }
        }
        {
            Daten lhsDaten;
            lhsDaten = this.getDaten();
            Daten rhsDaten;
            rhsDaten = that.getDaten();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "daten", lhsDaten), LocatorUtils.property(thatLocator, "daten", rhsDaten), lhsDaten, rhsDaten, (this.daten!= null), (that.daten!= null))) {
                return false;
            }
        }
        {
            Foto.Location lhsLocation;
            lhsLocation = this.getLocation();
            Foto.Location rhsLocation;
            rhsLocation = that.getLocation();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "location", lhsLocation), LocatorUtils.property(thatLocator, "location", rhsLocation), lhsLocation, rhsLocation, (this.location!= null), (that.location!= null))) {
                return false;
            }
        }
        return true;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(Object object) {
        final EqualsStrategy2 strategy = JAXBEqualsStrategy.INSTANCE;
        return equals(null, null, object, strategy);
    }

    /**
     *
     *                     Java enum for gruppe attribute in <foto> elements.
     *
     *
Java class for null.
     *
     *
The following schema fragment specifies the expected content contained within this class.
     *

     *

     * <simpleType>
     *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
     *     <enumeration value="EXTERN"/>
     *     <enumeration value="REMOTE"/>
     *   </restriction>
     * </simpleType>
     *

     *
     */
    @XmlType(name = "")
    @XmlEnum
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public enum Location {

        EXTERN,
        REMOTE;

        public String value() {
            return name();
        }

        public static Foto.Location fromValue(String v) {
            return valueOf(v);
        }

    }

}


/*
 * Copyright 2015-2018 OpenEstate.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openestate.io.openimmo;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.StringUtils;
import org.jaxen.JaxenException;
import org.openestate.io.core.XmlUtils;
import org.openestate.io.openimmo.xml.OpenimmoFeedback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * OpenImmo-XML document with a <openimmo_feedback> root element.
 *
 * @author Andreas Rudolph
 * @since 1.0
 */
@SuppressWarnings("WeakerAccess")
public class OpenImmoFeedbackDocument extends OpenImmoDocument<OpenimmoFeedback> {
    @SuppressWarnings("unused")
    private final static Logger LOGGER = LoggerFactory.getLogger(OpenImmoFeedbackDocument.class);

    /**
     * Create from a {@link Document}.
     *
     * @param document the document to create from
     */
    public OpenImmoFeedbackDocument(Document document) {
        super(document);
        if (!isReadable(document))
            throw new IllegalArgumentException("The provided document is invalid!");
    }

    @Override
    public OpenImmoVersion getDocumentVersion() {
        String version;
        try {
            Document doc = this.getDocument();

            Element node = (Element) XmlUtils
                    .newXPath("/io:openimmo_feedback/io:version", doc)
                    .selectSingleNode(doc);

            // versions older then 1.2.4 do not support the <version> element
            // - version 1.2.3 is assumed, when no <version> element is present and
            //   an empty namespace is used
            // - version 1.2.0 is assumed, when no <version> element is present and
            //   the old namespace is used
            if (node == null) {
                Element root = XmlUtils.getRootElement(doc);
                return (OpenImmoUtils.OLD_NAMESPACE.equalsIgnoreCase(root.getNamespaceURI())) ?
                        OpenImmoVersion.V1_2_0 : OpenImmoVersion.V1_2_3;
            }

            version = StringUtils.trimToNull(node.getTextContent());
            if (version == null) {
                LOGGER.warn("Can't find version information in the XML document!");
                //System.out.println( "----------------------------" );
                //try
                //{
                //  DocumentUtils.write( doc, System.out );
                //}
                //catch (Exception ex)
                //{
                //  LOGGER.error( "Can't write XML document!" );
                //  LOGGER.error( "> " + ex.getLocalizedMessage(), ex );
                //}
                //System.out.println( "----------------------------" );
                return null;
            }
        } catch (JaxenException ex) {
            LOGGER.error("Can't evaluate XPath expression!");
            LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            return null;
        }

        OpenImmoVersion v = OpenImmoVersion.detectFromString(version);
        if (v != null) return v;

        LOGGER.warn("The provided version (" + version + ") is not supported!");
        return null;
    }

    /**
     * Checks, if a {@link Document} is readable as a
     * {@link OpenImmoFeedbackDocument}.
     *
     * @param doc document to check
     * @return true, if the document is usable, otherwise false
     */
    public static boolean isReadable(Document doc) {
        Element root = XmlUtils.getRootElement(doc);
        return "openimmo_feedback".equals(root.getLocalName());
    }

    /**
     * Creates an empty {@link OpenImmoFeedbackDocument}.
     *
     * @return created document
     * @throws ParserConfigurationException if the parser is not properly configured
     * @throws JAXBException                if a problem with JAXB occurred
     */
    public static OpenImmoFeedbackDocument newDocument() throws ParserConfigurationException, JAXBException {
        return newDocument(OpenImmoUtils.getFactory().createOpenimmoFeedback());
    }

    /**
     * Creates a {@link OpenImmoFeedbackDocument} from a
     * {@link OpenimmoFeedback} object.
     *
     * @param feedback Java object, that represents the <openimmo_feedback> root element
     * @return created document
     * @throws ParserConfigurationException if the parser is not properly configured
     * @throws JAXBException                if a problem with JAXB occurred
     */
    public static OpenImmoFeedbackDocument newDocument(OpenimmoFeedback feedback) throws ParserConfigurationException, JAXBException {
        if (StringUtils.isBlank(feedback.getVersion()))
            feedback.setVersion(OpenImmoUtils.VERSION.toReadableVersion());

        Document document = XmlUtils.newDocument();
        OpenImmoUtils.createMarshaller("UTF-8", true).marshal(feedback, document);
        return new OpenImmoFeedbackDocument(document);
    }

    @Override
    public void setDocumentVersion(OpenImmoVersion version) {
        try {
            Document doc = this.getDocument();

            String currentVersion = StringUtils.trimToEmpty(XmlUtils
                    .newXPath("/io:openimmo/io:uebertragung/@version", doc)
                    .stringValueOf(doc));
            String[] ver = StringUtils.split(currentVersion, "/", 2);

            Element node = (Element) XmlUtils
                    .newXPath("/io:openimmo_feedback/io:version", doc)
                    .selectSingleNode(doc);

            // versions older then 1.2.4 do not support the <version> element
            if (OpenImmoVersion.V1_2_4.isNewerThen(version)) {
                if (node != null) {
                    Element root = XmlUtils.getRootElement(doc);
                    root.removeChild(node);
                }
                return;
            }

            if (node == null) {
                Element parentNode = (Element) XmlUtils
                        .newXPath("/io:openimmo_feedback", doc)
                        .selectSingleNode(doc);
                if (parentNode == null) {
                    LOGGER.warn("Can't find an <openimmo_feedback> element in the document!");
                    return;
                }
                node = doc.createElement("version");
                parentNode.insertBefore(node, parentNode.getFirstChild());
            }

            String newVersion = version.toReadableVersion();
            if (ver.length > 1) newVersion += "/" + ver[1];
            node.setTextContent(newVersion);
        } catch (JaxenException ex) {
            LOGGER.error("Can't evaluate XPath expression!");
            LOGGER.error("> " + ex.getLocalizedMessage(), ex);
        }
    }

    /**
     * Creates a {@link OpenimmoFeedback} object from the contained
     * {@link Document}.
     *
     * @return created object, that represents the <openimmo_feedback> root element
     * @throws JAXBException if a problem with JAXB occurred
     */
    @Override
    public OpenimmoFeedback toObject() throws JAXBException {
        this.upgradeToLatestVersion();
        return (OpenimmoFeedback) OpenImmoUtils.createUnmarshaller().unmarshal(this.getDocument());
    }
}


/*
 * Copyright 2015-2018 OpenEstate.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openestate.io.kyero;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.StringUtils;
import org.jaxen.JaxenException;
import org.openestate.io.core.XmlConvertableDocument;
import org.openestate.io.core.XmlUtils;
import org.openestate.io.kyero.xml.Root;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * Kyero-XML document with a <root> root element.
 *
 * @author Andreas Rudolph
 * @since 1.0
 */
@SuppressWarnings("WeakerAccess")
public class KyeroDocument extends XmlConvertableDocument<Root, KyeroVersion> {
    @SuppressWarnings("unused")
    private final static Logger LOGGER = LoggerFactory.getLogger(KyeroDocument.class);

    /**
     * Create from a {@link Document}.
     *
     * @param document the document to create from
     */
    public KyeroDocument(Document document) {
        super(document);
        if (!isReadable(document))
            throw new IllegalArgumentException("The provided document is invalid!");
    }

    @Override
    public KyeroVersion getDocumentVersion() {
        String version;
        try {
            Document doc = this.getDocument();
            version = StringUtils.trimToNull(XmlUtils
                    .newXPath("/io:root/io:kyero/io:feed_version/text()", doc)
                    .stringValueOf(doc));
            if (version == null) {
                LOGGER.warn("Can't find version information in the XML document!");
                //System.out.println( "----------------------------" );
                //try
                //{
                //  DocumentUtils.write( doc, System.out );
                //}
                //catch (Exception ex)
                //{
                //  LOGGER.error( "Can't write XML document!" );
                //  LOGGER.error( "> " + ex.getLocalizedMessage(), ex );
                //}
                //System.out.println( "----------------------------" );
                return null;
            }
        } catch (JaxenException ex) {
            LOGGER.error("Can't evaluate XPath expression!");
            LOGGER.error("> " + ex.getLocalizedMessage(), ex);
            return null;
        }

        KyeroVersion v = KyeroVersion.detectFromString(version);
        if (v != null) return v;

        LOGGER.warn("The provided version (" + version + ") is not supported!");
        return null;
    }

    @Override
    public KyeroVersion getLatestVersion() {
        return KyeroVersion.V3;
    }

    /**
     * Checks, if a {@link Document} is readable as a {@link KyeroDocument}.
     *
     * @param doc document to check
     * @return true, if the document is usable, otherwise false
     */
    public static boolean isReadable(Document doc) {
        Element root = XmlUtils.getRootElement(doc);
        return "root".equals(root.getLocalName());
    }

    /**
     * Creates an empty {@link KyeroDocument}.
     *
     * @return created document
     * @throws ParserConfigurationException if the parser is not properly configured
     * @throws JAXBException                if a problem with JAXB occurred
     */
    public static KyeroDocument newDocument() throws ParserConfigurationException, JAXBException {
        return newDocument(KyeroUtils.getFactory().createRoot());
    }

    /**
     * Creates a {@link KyeroDocument} from a {@link Root} object.
     *
     * @param root Java object, that represents the <root> root element
     * @return created document
     * @throws ParserConfigurationException if the parser is not properly configured
     * @throws JAXBException                if a problem with JAXB occurred
     */
    public static KyeroDocument newDocument(Root root) throws ParserConfigurationException, JAXBException {
        if (root.getKyero() == null)
            root.setKyero(KyeroUtils.getFactory().createKyeroType());
        if (StringUtils.isBlank(root.getKyero().getFeedVersion()))
            root.getKyero().setFeedVersion(KyeroUtils.VERSION.toXmlVersion());

        Document document = XmlUtils.newDocument();
        KyeroUtils.createMarshaller("UTF-8", true).marshal(root, document);
        return new KyeroDocument(document);
    }

    @Override
    public void setDocumentVersion(KyeroVersion version) {
        try {
            Document doc = this.getDocument();

            Element node = (Element) XmlUtils
                    .newXPath("/io:root/io:kyero/io:feed_version", doc)
                    .selectSingleNode(doc);
            if (node == null) {
                Element parentNode = (Element) XmlUtils
                        .newXPath("/io:root/io:kyero", doc)
                        .selectSingleNode(doc);
                if (parentNode == null) {
                    Element grandParentNode = (Element) XmlUtils
                            .newXPath("/io:root", doc)
                            .selectSingleNode(doc);
                    if (grandParentNode == null) {
                        LOGGER.warn("Can't find a <root> element in the document!");
                        return;
                    }
                    parentNode = doc.createElement("kyero");
                    grandParentNode.insertBefore(parentNode, grandParentNode.getFirstChild());
                }
                node = doc.createElement("feed_version");
                parentNode.insertBefore(node, parentNode.getFirstChild());
            }

            String newVersion = version.toXmlVersion();
            node.setTextContent(newVersion);
        } catch (JaxenException ex) {
            LOGGER.error("Can't evaluate XPath expression!");
            LOGGER.error("> " + ex.getLocalizedMessage(), ex);
        }
    }

    /**
     * Creates a {@link Root} object from the contained {@link Document}.
     *
     * @return created object, that represents the <root> root element
     * @throws JAXBException if a problem with JAXB occurred
     */
    @Override
    public Root toObject() throws JAXBException {
        this.upgradeToLatestVersion();
        return (Root) KyeroUtils.createUnmarshaller().unmarshal(this.getDocument());
    }
}


package org.openestate.io.openimmo.xml;

import java.io.Serializable;
import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.jvnet.jaxb2_commons.lang.CopyStrategy2;
import org.jvnet.jaxb2_commons.lang.CopyTo2;
import org.jvnet.jaxb2_commons.lang.Equals2;
import org.jvnet.jaxb2_commons.lang.EqualsStrategy2;
import org.jvnet.jaxb2_commons.lang.JAXBCopyStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBEqualsStrategy;
import org.jvnet.jaxb2_commons.lang.JAXBToStringStrategy;
import org.jvnet.jaxb2_commons.lang.ToString2;
import org.jvnet.jaxb2_commons.lang.ToStringStrategy2;
import org.jvnet.jaxb2_commons.locator.ObjectLocator;
import org.jvnet.jaxb2_commons.locator.util.LocatorUtils;

/**
 * Java class for <alter> element.
 *
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "alter")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
public class Alter implements Serializable, Cloneable, CopyTo2, Equals2, ToString2
{

    @XmlAttribute(name = "alter_attr")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    protected Alter.AlterAttr alterAttr;

    /**
     * Gets the value of the alterAttr property.
     *
     * @return
     *     possible object is
     *     {@link Alter.AlterAttr }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Alter.AlterAttr getAlterAttr() {
        return alterAttr;
    }

    /**
     * Sets the value of the alterAttr property.
     *
     * @param value
     *     allowed object is
     *     {@link Alter.AlterAttr }
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public void setAlterAttr(Alter.AlterAttr value) {
        this.alterAttr = value;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public String toString() {
        final ToStringStrategy2 strategy = JAXBToStringStrategy.INSTANCE;
        final StringBuilder buffer = new StringBuilder();
        append(null, buffer, strategy);
        return buffer.toString();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder append(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        strategy.appendStart(locator, this, buffer);
        appendFields(locator, buffer, strategy);
        strategy.appendEnd(locator, this, buffer);
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public StringBuilder appendFields(ObjectLocator locator, StringBuilder buffer, ToStringStrategy2 strategy) {
        {
            Alter.AlterAttr theAlterAttr;
            theAlterAttr = this.getAlterAttr();
            strategy.appendField(locator, this, "alterAttr", buffer, theAlterAttr, (this.alterAttr!= null));
        }
        return buffer;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object clone() {
        return copyTo(createNewInstance());
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(Object target) {
        final CopyStrategy2 strategy = JAXBCopyStrategy.INSTANCE;
        return copyTo(null, target, strategy);
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object copyTo(ObjectLocator locator, Object target, CopyStrategy2 strategy) {
        final Object draftCopy = ((target == null)?createNewInstance():target);
        if (draftCopy instanceof Alter) {
            final Alter copy = ((Alter) draftCopy);
            {
                Boolean alterAttrShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.alterAttr!= null));
                if (alterAttrShouldBeCopiedAndSet == Boolean.TRUE) {
                    Alter.AlterAttr sourceAlterAttr;
                    sourceAlterAttr = this.getAlterAttr();
                    Alter.AlterAttr copyAlterAttr = ((Alter.AlterAttr) strategy.copy(LocatorUtils.property(locator, "alterAttr", sourceAlterAttr), sourceAlterAttr, (this.alterAttr!= null)));
                    copy.setAlterAttr(copyAlterAttr);
                } else {
                    if (alterAttrShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.alterAttr = null;
                    }
                }
            }
        }
        return draftCopy;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public Object createNewInstance() {
        return new Alter();
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy2 strategy) {
        if ((object == null)||(this.getClass()!= object.getClass())) {
            return false;
        }
        if (this == object) {
            return true;
        }
        final Alter that = ((Alter) object);
        {
            Alter.AlterAttr lhsAlterAttr;
            lhsAlterAttr = this.getAlterAttr();
            Alter.AlterAttr rhsAlterAttr;
            rhsAlterAttr = that.getAlterAttr();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "alterAttr", lhsAlterAttr), LocatorUtils.property(thatLocator, "alterAttr", rhsAlterAttr), lhsAlterAttr, rhsAlterAttr, (this.alterAttr!= null), (that.alterAttr!= null))) {
                return false;
            }
        }
        return true;
    }

    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public boolean equals(Object object) {
        final EqualsStrategy2 strategy = JAXBEqualsStrategy.INSTANCE;
        return equals(null, null, object, strategy);
    }

    /**
     *
     *                     Java enum for alter_attr attribute in <alter> elements.
     *
     *
Java class for null.
     *
     *
The following schema fragment specifies the expected content contained within this class.
     *

     *

     * <simpleType>
     *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
     *     <enumeration value="ALTBAU"/>
     *     <enumeration value="NEUBAU"/>
     *   </restriction>
     * </simpleType>
     *

     *
     */
    @XmlType(name = "")
    @XmlEnum
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:54:50+02:00", comments = "JAXB RI v2.2.11")
    public enum AlterAttr {

        ALTBAU,
        NEUBAU;

        public String value() {
            return name();
        }

        public static Alter.AlterAttr fromValue(String v) {
            return valueOf(v);
        }

    }

}