/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 io.arivera.oss.embedded.rabbitmq.apache.commons.io;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;

/**
 * General file manipulation utilities.
 *

 * Facilities are provided in the following areas:
 *

 *
writing to a file
 *
reading from a file
 *
make a directory including parent directories
 *
copying files and directories
 *
deleting files and directories
 *
converting to and from a URL
 *
listing files and directories by filter and extension
 *
comparing file content
 *
file last changed date
 *
calculating a checksum
 *

 *

 * Note that a specific charset should be specified whenever possible.
 * Relying on the platform default means that the code is Locale-dependent.
 * Only use the default if the files are known to always use the platform default.
 *

 * Origin of code: Excalibur, Alexandria, Commons-Utils
 *
 * @version $Id$
 */
@SuppressWarnings("all")
public class FileUtils {

  /**
   * Copies bytes from the URL source to a file
   * destination. The directories up to destination
   * will be created if they don't already exist. destination
   * will be overwritten if it already exists.
   *
   * @param source            the URL to copy bytes from, must not be {@code null}
   * @param destination       the non-directory File to write bytes to
   *                          (possibly overwriting), must not be {@code null}
   * @param connectionTimeout the number of milliseconds until this method
   *                          will timeout if no connection could be established to the source
   * @param readTimeout       the number of milliseconds until this method will
   *                          timeout if no data could be read from the source
   * @throws IOException if source URL cannot be opened
   * @throws IOException if destination is a directory
   * @throws IOException if destination cannot be written
   * @throws IOException if destination needs creating but can't be
   * @throws IOException if an IO error occurs during copying
   * @since 2.0
   */
  public static void copyURLToFile(URL source, File destination,
                                   int connectionTimeout, int readTimeout) throws IOException {
    copyUrlToFile(source, destination, connectionTimeout, readTimeout, null);
  }

  /**
   * Copies bytes from the URL source to a file
   * destination. The directories up to destination
   * will be created if they don't already exist. destination
   * will be overwritten if it already exists.
   *
   * @param source            the URL to copy bytes from, must not be {@code null}
   * @param destination       the non-directory File to write bytes to
   *                          (possibly overwriting), must not be {@code null}
   * @param connectionTimeout the number of milliseconds until this method
   *                          will timeout if no connection could be established to the source
   * @param readTimeout       the number of milliseconds until this method will
   *                          timeout if no data could be read from the source
   * @param proxy             the proxy to use to open connection
   * @throws IOException if source URL cannot be opened
   * @throws IOException if destination is a directory
   * @throws IOException if destination cannot be written
   * @throws IOException if destination needs creating but can't be
   * @throws IOException if an IO error occurs during copying
   */
  public static void copyUrlToFile(URL source, File destination,
                                  int connectionTimeout, int readTimeout, Proxy proxy) throws IOException {
    URLConnection connection;
    if (proxy == null) {
      connection = source.openConnection();
    } else {
      connection = source.openConnection(proxy);
    }
    connection.setConnectTimeout(connectionTimeout);
    connection.setReadTimeout(readTimeout);
    InputStream input = connection.getInputStream();
    copyInputStreamToFile(input, destination);
  }

  /**
   * Copies bytes from an {@link InputStream} source to a file
   * destination. The directories up to destination
   * will be created if they don't already exist. destination
   * will be overwritten if it already exists.
   *
   * @param source      the InputStream to copy bytes from, must not be {@code null}
   * @param destination the non-directory File to write bytes to
   *                    (possibly overwriting), must not be {@code null}
   * @throws IOException if destination is a directory
   * @throws IOException if destination cannot be written
   * @throws IOException if destination needs creating but can't be
   * @throws IOException if an IO error occurs during copying
   * @since 2.0
   */
  public static void copyInputStreamToFile(InputStream source, File destination) throws IOException {
    try {
      FileOutputStream output = openOutputStream(destination);
      try {
        copy(source, output);
        output.close(); // don't swallow close Exception if copy completes normally
      } finally {
        closeQuietly(output);
      }
    } finally {
      closeQuietly(source);
    }
  }

  public static FileOutputStream openOutputStream(File file) throws IOException {
    if (file.exists()) {
      if (file.isDirectory()) {
        throw new IOException("File \'" + file + "\' exists but is a directory");
      }

      if (!file.canWrite()) {
        throw new IOException("File \'" + file + "\' cannot be written to");
      }
    } else {
      File parent = file.getParentFile();
      if (parent != null && !parent.exists() && !parent.mkdirs()) {
        throw new IOException("File \'" + file + "\' could not be created");
      }
    }

    return new FileOutputStream(file);
  }

  public static int copy(InputStream input, OutputStream output) throws IOException {
    long count = copyLarge(input, output);
    return count > 2147483647L ? -1 : (int) count;
  }

  public static long copyLarge(InputStream input, OutputStream output) throws IOException {
    byte[] buffer = new byte[4096];
    long count = 0L;

    int n1;
    for (boolean n = false; -1 != (n1 = input.read(buffer)); count += (long) n1) {
      output.write(buffer, 0, n1);
    }

    return count;
  }

  public static void closeQuietly(OutputStream output) {
    try {
      if (output != null) {
        output.close();
      }
    } catch (IOException var2) {
      ;
    }

  }

  public static void closeQuietly(InputStream input) {
    try {
      if (input != null) {
        input.close();
      }
    } catch (IOException var2) {
      ;
    }

  }
}


package io.arivera.oss.embedded.rabbitmq.helpers;

import io.arivera.oss.embedded.rabbitmq.EmbeddedRabbitMqConfig;
import io.arivera.oss.embedded.rabbitmq.bin.RabbitMqCommand;
import io.arivera.oss.embedded.rabbitmq.bin.RabbitMqCommandException;
import io.arivera.oss.embedded.rabbitmq.bin.RabbitMqServer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zeroturnaround.exec.ProcessResult;
import org.zeroturnaround.exec.listener.ProcessListener;
import org.zeroturnaround.exec.stream.LogOutputStream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

public class StartupHelper implements Callable<Future<ProcessResult>> {

  public static final String BROKER_STARTUP_COMPLETED = ".*completed with \\d+ plugins.*";
  private final EmbeddedRabbitMqConfig config;

  public StartupHelper(EmbeddedRabbitMqConfig config) {
    this.config = config;
  }

  /**
   * Starts the RabbitMQ Server and blocks the current thread until the server is confirmed to have started.
   * <p>
   * This is useful to ensure no other interactions happen with the RabbitMQ Server until it's safe to do so
   *
   * @return an unfinished future representing the eventual result of the {@code rabbitmq-server} process running in "foreground".
   * @throws StartupException if anything fails while attempting to start and confirm successful initialization.
   * @see ShutdownHelper
   */
  @Override
  public Future<ProcessResult> call() throws StartupException {
    PatternFinderOutputStream initializationWatcher = new PatternFinderOutputStream(BROKER_STARTUP_COMPLETED);

    // Inform the initializationWatcher if the process ends before the expected output is produced.
    PublishingProcessListener rabbitMqProcessListener = new PublishingProcessListener();
    rabbitMqProcessListener.addSubscriber(initializationWatcher);

    Future<ProcessResult> resultFuture = startProcess(initializationWatcher, rabbitMqProcessListener);
    waitForConfirmation(initializationWatcher);

    return resultFuture;
  }

  private Future<ProcessResult> startProcess(PatternFinderOutputStream initializationWatcher,
                                             PublishingProcessListener rabbitMqProcessListener) {
    Future<ProcessResult> resultFuture;
    try {
      resultFuture = new RabbitMqServer(config)
          .writeOutputTo(initializationWatcher)
          .listeningToEventsWith(rabbitMqProcessListener)
          .start();
    } catch (RabbitMqCommandException e) {
      throw new StartupException("Could not start RabbitMQ Server", e);
    }
    return resultFuture;
  }

  private void waitForConfirmation(PatternFinderOutputStream initializationWatcher) {
    long timeout = config.getRabbitMqServerInitializationTimeoutInMillis();
    boolean match = initializationWatcher.waitForMatch(timeout, TimeUnit.MILLISECONDS);

    if (!match) {
      throw new StartupException(
          "Could not confirm RabbitMQ Server initialization completed successfully within " + timeout + "ms");
    }
  }

  /**
   * Notifies subscribers of process termination so they don't have to rely on blocking {@link Future#get()} of
   * {@link ProcessResult}s, which is returned by {@link RabbitMqCommand}s.
   */
  static class PublishingProcessListener extends ProcessListener {

    interface Subscriber {
      void processFinished(int exitValue);
    }

    private final List<Subscriber> subscribers;

    public PublishingProcessListener(Subscriber... subscribers) {
      this.subscribers = new ArrayList<>(Arrays.asList(subscribers));
    }

    @Override
    public void afterFinish(Process process, ProcessResult result) {
      super.afterFinish(process, result);
      for (Subscriber subscriber : subscribers) {
        subscriber.processFinished(result.getExitValue());
      }
    }

    public void addSubscriber(Subscriber subscriber) {
      this.subscribers.add(subscriber);
    }

  }

  /**
   * An output stream that compares each line with a given pattern.
   *
   * This class offers the ability to wait until the pattern is found or the given amount of time has passed.
   */
  static class PatternFinderOutputStream extends LogOutputStream implements PublishingProcessListener.Subscriber {

    private static final Logger LOGGER = LoggerFactory.getLogger(PatternFinderOutputStream.class);

    private final Pattern pattern;
    private final Semaphore lock;
    private boolean matchFound;

    public PatternFinderOutputStream(String initializationMarkerPattern) {
      this(Pattern.compile(initializationMarkerPattern, Pattern.CASE_INSENSITIVE));
    }

    public PatternFinderOutputStream(Pattern initializationMarkerPattern) {
      try {
        lock = new Semaphore(1);
        lock.acquire();
      } catch (InterruptedException e) {
        throw new IllegalStateException("Could not acquire a lock we create right above?", e);
      }
      pattern = initializationMarkerPattern;
      matchFound = false;
    }

    @Override
    protected void processLine(String line) {
      if (pattern.matcher(line).matches()) {
        LOGGER.trace("Pattern '{}' found in line: {}", pattern, line);
        matchFound = true;
        lock.release();
      }
      LOGGER.trace("Pattern '{}' NOT found in line: {}", pattern, line);
    }

    @Override
    public void processFinished(int exitValue) {
      LOGGER.debug("No more output is expected since process finished (exit code: {})", exitValue);
      lock.release();
    }

    public boolean waitForMatch(long duration, TimeUnit timeUnit) {
      try {
        boolean acquired = lock.tryAcquire(duration, timeUnit);
        if (!acquired) {
          LOGGER.info("Waited for {} {} for pattern '{}' to appear but it didn't.", duration, timeUnit, pattern );
        }
      } catch (InterruptedException e) {
        LOGGER.warn("Error while waiting for process output that matches the pattern '{}'", pattern);
      }
      return isMatchFound();
    }

    public boolean isMatchFound() {
      return matchFound;
    }
  }
}


package io.arivera.oss.embedded.rabbitmq.bin.plugins;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Plugin implements Comparable<Plugin> {

  static final Pattern LIST_OUTPUT_PATTERN = Pattern.compile("\\s*\\[(.*)]\\s+(\\w+)\\s+(\\S+)\\s*");

  private static final Logger LOGGER = LoggerFactory.getLogger(Plugin.class);

  private String pluginName;
  private EnumSet<State> status;
  private String version;

  private Plugin(String pluginName, EnumSet<State> state, String version) {
    this.pluginName = pluginName;
    this.status = state;
    this.version = version;
  }

  /**
   * @param strings all the lines to parse. Those that can't be parsed won't be part of the resulting list.
   */
  public static List<Plugin> fromStrings(Collection<String> strings) {
    List<Plugin> plugins = new ArrayList<>(strings.size());
    for (String string : strings) {
      Plugin plugin = fromString(string);
      if (plugin != null) {
        plugins.add(plugin);
      }
    }
    return plugins;
  }

  /**
   * @param outputLine as generated by the command line {@code rabbitmq-plugins groupedList}
   *
   * @return null if output can't be parsed.
   */
  public static Plugin fromString(String outputLine) {
    Matcher matcher = LIST_OUTPUT_PATTERN.matcher(outputLine);
    if (!matcher.matches()) {
      return null;
    }
    String state = matcher.group(1);
    String pluginName = matcher.group(2);
    String version = matcher.group(3);

    return new Plugin(pluginName, State.fromString(state), version);
  }

  public String getName() {
    return pluginName;
  }

  public EnumSet<State> getState() {
    return status;
  }

  public String getVersion() {
    return version;
  }

  @Override
  public boolean equals(Object other) {
    if (this == other) {
      return true;
    }
    if (other == null || getClass() != other.getClass()) {
      return false;
    }
    Plugin that = (Plugin) other;
    return Objects.equals(pluginName, that.pluginName);
  }

  @Override
  public int hashCode() {
    return Objects.hash(pluginName);
  }

  @Override
  public int compareTo(Plugin other) {
    return this.pluginName.compareTo(other.pluginName);
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder("Plugin{");
    sb.append("name='").append(pluginName).append('\'');
    sb.append(", status=").append(status);
    sb.append(", version='").append(version).append('\'');
    sb.append('}');
    return sb.toString();
  }

  public enum State {
    ENABLED_EXPLICITLY, ENABLED_IMPLICITLY, NOT_ENABLED,
    RUNNING, NOT_RUNNING;

    /**
     * Parses a string as given by the command line output from {@code rabbitmq-plugins list} for the characters in
     * between brackets.
     */
    public static EnumSet<State> fromString(String string) {
      EnumSet<State> pluginStatuses = EnumSet.noneOf(State.class);

      char[] chars = string.toCharArray();

      if (chars.length != 2) {
        LOGGER.warn("Parsing of Plugin State might not be accurate since we expect 2 symbols representing: {}",
            Arrays.asList(State.values()));
      }

      if (chars.length <= 0) {
        return pluginStatuses;
      }

      char enabledCharacter = chars[0];
      switch (enabledCharacter) {
        case ' ':
          pluginStatuses.add(NOT_ENABLED);
          break;
        case 'e':
          pluginStatuses.add(ENABLED_IMPLICITLY);
          break;
        case 'E':
          pluginStatuses.add(ENABLED_EXPLICITLY);
          break;
        default:
          LOGGER.warn("Could not parse symbol '{}' for enabled state in: {}", enabledCharacter, string);
      }

      if (chars.length < 2) {
        return pluginStatuses;
      }

      char runningCharacter = string.charAt(1);
      switch (runningCharacter) {
        case '*':
          pluginStatuses.add(RUNNING);
          break;
        case ' ':
          pluginStatuses.add(NOT_RUNNING);
          break;
        default:
          LOGGER.warn("Could not parse symbol '{}' for run state in: {}", runningCharacter, string);
      }

      return pluginStatuses;
    }

  }
}


package me.kevingleason.pnwebrtc;

import org.webrtc.MediaConstraints;
import org.webrtc.PeerConnection;

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

/**
 *
Define {@link org.webrtc.MediaConstraints} and {@link org.webrtc.PeerConnection.IceServer} for WebRTC PeerConnections

 *

 * Author:  Kevin Gleason - Boston College '16
 * File:    PnSignalingParams.java
 * Date:    7/20/15
 * Use:     Hold the signaling parameters of a WebRTC PeerConnection
 * © 2009 - 2015 PubNub, Inc.
 *

 *
IceServers allow Trickling, so they are not final.

 *
 */
public class PnSignalingParams {
    public List<PeerConnection.IceServer> iceServers;
    public final MediaConstraints pcConstraints;
    public final MediaConstraints videoConstraints;
    public final MediaConstraints audioConstraints;

    public PnSignalingParams(
            List<PeerConnection.IceServer> iceServers,
            MediaConstraints pcConstraints,
            MediaConstraints videoConstraints,
            MediaConstraints audioConstraints) {
        this.iceServers       = (iceServers==null)       ? defaultIceServers()       : iceServers;
        this.pcConstraints    = (pcConstraints==null)    ? defaultPcConstraints()    : pcConstraints;
        this.videoConstraints = (videoConstraints==null) ? defaultVideoConstraints() : videoConstraints;
        this.audioConstraints = (audioConstraints==null) ? defaultAudioConstraints() : audioConstraints;
    }

    /**
     * Default Ice Servers, but specified parameters.
     * @param pcConstraints
     * @param videoConstraints
     * @param audioConstraints
     */
    public PnSignalingParams(
            MediaConstraints pcConstraints,
            MediaConstraints videoConstraints,
            MediaConstraints audioConstraints) {
        this.iceServers       = PnSignalingParams.defaultIceServers();
        this.pcConstraints    = (pcConstraints==null)    ? defaultPcConstraints()    : pcConstraints;
        this.videoConstraints = (videoConstraints==null) ? defaultVideoConstraints() : videoConstraints;
        this.audioConstraints = (audioConstraints==null) ? defaultAudioConstraints() : audioConstraints;
    }

    /**
     * Default media params, but specified Ice Servers
     * @param iceServers
     */
    public PnSignalingParams(List<PeerConnection.IceServer> iceServers) {
        this.iceServers       = defaultIceServers();
        this.pcConstraints    = defaultPcConstraints();
        this.videoConstraints = defaultVideoConstraints();
        this.audioConstraints = defaultAudioConstraints();
        addIceServers(iceServers);
    }

    /**
     * Default media params and ICE servers.
     */
    public PnSignalingParams() {
        this.iceServers       = defaultIceServers();
        this.pcConstraints    = defaultPcConstraints();
        this.videoConstraints = defaultVideoConstraints();
        this.audioConstraints = defaultAudioConstraints();
    }

    /**
     * The default parameters for media constraints. Might have to tweak in future.
     * @return default parameters
     */
    public static PnSignalingParams defaultInstance() {
        MediaConstraints pcConstraints    = PnSignalingParams.defaultPcConstraints();
        MediaConstraints videoConstraints = PnSignalingParams.defaultVideoConstraints();
        MediaConstraints audioConstraints = PnSignalingParams.defaultAudioConstraints();
        List<PeerConnection.IceServer> iceServers = PnSignalingParams.defaultIceServers();
        return new PnSignalingParams(iceServers, pcConstraints, videoConstraints, audioConstraints);
    }

    private static MediaConstraints defaultPcConstraints(){
        MediaConstraints pcConstraints = new MediaConstraints();
        pcConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
        pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
        return pcConstraints;
    }

    private static MediaConstraints defaultVideoConstraints(){
        MediaConstraints videoConstraints = new MediaConstraints();
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxWidth","1280"));
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("maxHeight","720"));
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minWidth", "640"));
        videoConstraints.mandatory.add(new MediaConstraints.KeyValuePair("minHeight","480"));
        return videoConstraints;
    }

    private static MediaConstraints defaultAudioConstraints(){
        MediaConstraints audioConstraints = new MediaConstraints();
        return audioConstraints;
    }

    public static List<PeerConnection.IceServer> defaultIceServers(){
        List<PeerConnection.IceServer> iceServers = new ArrayList<PeerConnection.IceServer>(25);
        iceServers.add(new PeerConnection.IceServer("stun:stun.l.google.com:19302"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.services.mozilla.com"));
        iceServers.add(new PeerConnection.IceServer("turn:turn.bistri.com:80", "homeo", "homeo"));
        iceServers.add(new PeerConnection.IceServer("turn:turn.anyfirewall.com:443?transport=tcp", "webrtc", "webrtc"));

        // Extra Defaults - 19 STUN servers + 4 initial = 23 severs (+2 padding) = Array cap 25
        iceServers.add(new PeerConnection.IceServer("stun:stun1.l.google.com:19302"));
        iceServers.add(new PeerConnection.IceServer("stun:stun2.l.google.com:19302"));
        iceServers.add(new PeerConnection.IceServer("stun:stun3.l.google.com:19302"));
        iceServers.add(new PeerConnection.IceServer("stun:stun4.l.google.com:19302"));
        iceServers.add(new PeerConnection.IceServer("stun:23.21.150.121"));
        iceServers.add(new PeerConnection.IceServer("stun:stun01.sipphone.com"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.ekiga.net"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.fwdnet.net"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.ideasip.com"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.iptel.org"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.rixtelecom.se"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.schlund.de"));
        iceServers.add(new PeerConnection.IceServer("stun:stunserver.org"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.softjoys.com"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.voiparound.com"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.voipbuster.com"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.voipstunt.com"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.voxgratia.org"));
        iceServers.add(new PeerConnection.IceServer("stun:stun.xten.com"));

        return iceServers;
    }

    /**
     * Append default servers to the end of given list and set as iceServers instance variable
     * @param iceServers List of iceServers
     */
    public void addIceServers(List<PeerConnection.IceServer> iceServers){
        if(this.iceServers!=null) {
            iceServers.addAll(this.iceServers);
        }
        this.iceServers = iceServers;
    }

    /**
     * Instantiate iceServers if they are not already, and add Ice Server to beginning of list.
     * @param iceServers Ice Server to add
     */
    public void addIceServers(PeerConnection.IceServer iceServers){
        if (this.iceServers == null){
            this.iceServers = new ArrayList<PeerConnection.IceServer>();
        }
        this.iceServers.add(0, iceServers);
    }
}


package me.kevingleason.pnwebrtc;

import android.util.Log;

import com.pubnub.api.Callback;
import com.pubnub.api.Pubnub;
import com.pubnub.api.PubnubError;
import com.pubnub.api.PubnubException;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.IceCandidate;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.SessionDescription;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 *
PeerConnection manager for {@link me.kevingleason.pnwebrtc.PnRTCClient}

 *

 * Author:  Kevin Gleason - Boston College '16
 * File:    PnPeerConnectionClient.java
 * Date:    7/20/15
 * Use:     WebRTC PeerConnection Manager
 * © 2009 - 2015 PubNub, Inc.
 *

 *
 * {@link PnPeerConnectionClient} is used to manage peer connections.
 */
public class PnPeerConnectionClient {
    private SessionDescription localSdp  = null; // either offer or answer SDP
    private MediaStream localMediaStream = null;
    PeerConnectionFactory pcFactory;
    PnRTCListener mRtcListener;
    PnSignalingParams signalingParams;
    int MAX_CONNECTIONS = Integer.MAX_VALUE;

    private Pubnub mPubNub;
    private PnRTCReceiver mSubscribeReceiver;
    private Map<String,PnAction> actionMap;
    private Map<String,PnPeer> peers;
    private String id;

    public PnPeerConnectionClient(Pubnub pubnub, PnSignalingParams signalingParams, PnRTCListener rtcListener){
        this.mPubNub = pubnub;
        this.signalingParams = signalingParams;
        this.mRtcListener = rtcListener;
        this.pcFactory = new PeerConnectionFactory(); // TODO: Check it allowed, else extra param
        this.peers = new HashMap<String, PnPeer>();
        init();
    }

    private void init(){
        this.actionMap = new HashMap<String, PnAction>();
        this.actionMap.put(CreateOfferAction.TRIGGER,     new CreateOfferAction());
        this.actionMap.put(CreateAnswerAction.TRIGGER,    new CreateAnswerAction());
        this.actionMap.put(SetRemoteSDPAction.TRIGGER,    new SetRemoteSDPAction());
        this.actionMap.put(AddIceCandidateAction.TRIGGER, new AddIceCandidateAction());
        this.actionMap.put(PnUserHangupAction.TRIGGER,    new PnUserHangupAction());
        this.actionMap.put(PnUserMessageAction.TRIGGER,   new PnUserMessageAction());
        mSubscribeReceiver = new PnRTCReceiver();
    }

    boolean listenOn(String myId){  // Todo: return success?
        if (localMediaStream==null){       // Not true for streaming?
            mRtcListener.onDebug(new PnRTCMessage("Need to add media stream before you can connect."));
            return false;
        }
        if (this.id != null){  // Prevent listening on multiple channels.
            mRtcListener.onDebug(new PnRTCMessage("Already listening on " + this.id + ". Cannot have multiple connections."));
            return false;
        }
        this.id = myId;
        subscribe(myId);
        return true;
    }

    /**TODO: Add a max user threshold.
     * Connect with another user by their ID.
     * @param userId The user to establish a WebRTC connection with
     * @return boolean value of success
     */
    boolean connect(String userId) {
        if (!peers.containsKey(userId)) { // Prevents duplicate dials.
            if (peers.size() < MAX_CONNECTIONS) {
                PnPeer peer = addPeer(userId);
                peer.pc.addStream(this.localMediaStream);
                try {
                    actionMap.get(CreateOfferAction.TRIGGER).execute(userId, new JSONObject());
                } catch (JSONException e){
                    e.printStackTrace();
                    return false;
                }
                return true;
            }
        }
        this.mRtcListener.onDebug(new PnRTCMessage("CONNECT FAILED. Duplicate dial or max peer " +
                "connections exceeded. Max: " + MAX_CONNECTIONS + " Current: " + this.peers.size()));
        return false;
    }

    public void setRTCListener(PnRTCListener listener){
        this.mRtcListener = listener;
    }

    private void subscribe(String channel){
        try {
            mPubNub.subscribe(channel, this.mSubscribeReceiver);
        } catch (PubnubException e){
            e.printStackTrace();
        }
    }

    public void setLocalMediaStream(MediaStream localStream){
        this.localMediaStream = localStream;
        mRtcListener.onLocalStream(localStream);
    }

    public MediaStream getLocalMediaStream(){
        return this.localMediaStream;
    }

    private PnPeer addPeer(String id) {
        PnPeer peer = new PnPeer(id, this);
        peers.put(id, peer);
        return peer;
    }

    PnPeer removePeer(String id) {
        PnPeer peer = peers.get(id);
        peer.pc.close();
        return peers.remove(peer.id);
    }

    List<PnPeer> getPeers(){
        return new ArrayList<PnPeer>(this.peers.values());
    }

    /**
     * Close connection (hangup) no a certain peer.
     * @param id PnPeer id to close connection with
     */
    public void closeConnection(String id){
        JSONObject packet = new JSONObject();
        try {
            if (!this.peers.containsKey(id)) return;
            PnPeer peer = this.peers.get(id);
            peer.hangup();
            packet.put(PnRTCMessage.JSON_HANGUP, true);
            transmitMessage(id, packet);
            mRtcListener.onPeerConnectionClosed(peer);
        } catch (JSONException e){
            e.printStackTrace();
        }
    }

    /**
     * Close connections (hangup) on all open connections.
     */
    public void closeAllConnections() {
        Iterator<String> peerIds = this.peers.keySet().iterator();
        while (peerIds.hasNext()) {
            closeConnection(peerIds.next());
        }
    }

    /**
     * Send SDP Offers/Answers nd ICE candidates to peers.
     * @param toID The id or "number" that you wish to transmit a message to.
     * @param packet The JSON data to be transmitted
     */
    void transmitMessage(String toID, JSONObject packet){
        if (this.id==null){ // Not logged in. Put an error in the debug cb.
            mRtcListener.onDebug(new PnRTCMessage("Cannot transmit before calling Client.connect"));
        }
        try {
            JSONObject message = new JSONObject();
            message.put(PnRTCMessage.JSON_PACKET, packet);
            message.put(PnRTCMessage.JSON_ID, ""); //Todo: session id, unused in js SDK?
            message.put(PnRTCMessage.JSON_NUMBER, this.id);
            this.mPubNub.publish(toID, message, new Callback() {  // Todo: reconsider callback.
                @Override
                public void successCallback(String channel, Object message, String timetoken) {
                    mRtcListener.onDebug(new PnRTCMessage((JSONObject)message));
                }

                @Override
                public void errorCallback(String channel, PubnubError error) {
                    mRtcListener.onDebug(new PnRTCMessage(error.errorObject));
                }
            });
        } catch (JSONException e){
            e.printStackTrace();
        }
    }

    private interface PnAction{
        void execute(String peerId, JSONObject payload) throws JSONException;
    }

    private class CreateOfferAction implements PnAction{
        public static final String TRIGGER = "init";
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.d("COAction","CreateOfferAction");
            PnPeer peer = peers.get(peerId);
            peer.setDialed(true);
            peer.setType(PnPeer.TYPE_ANSWER);
            peer.pc.createOffer(peer, signalingParams.pcConstraints);
        }
    }

    private class CreateAnswerAction implements PnAction{
        public static final String TRIGGER = "offer";
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.d("CAAction","CreateAnswerAction");
            PnPeer peer = peers.get(peerId);
            peer.setType(PnPeer.TYPE_OFFER);
            peer.setStatus(PnPeer.STATUS_CONNECTED);
            SessionDescription sdp = new SessionDescription(
                    SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
                    payload.getString("sdp")
            );
            peer.pc.setRemoteDescription(peer, sdp);
            peer.pc.createAnswer(peer, signalingParams.pcConstraints);
        }
    }

    private class SetRemoteSDPAction implements PnAction{
        public static final String TRIGGER = "answer";
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.d("SRSAction","SetRemoteSDPAction");
            PnPeer peer = peers.get(peerId);
            SessionDescription sdp = new SessionDescription(
                    SessionDescription.Type.fromCanonicalForm(payload.getString("type")),
                    payload.getString("sdp")
            );
            peer.pc.setRemoteDescription(peer, sdp);
        }
    }

    private class AddIceCandidateAction implements PnAction{
        public static final String TRIGGER = "candidate";
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.d("AICAction","AddIceCandidateAction");
            PeerConnection pc = peers.get(peerId).pc;
            if (pc.getRemoteDescription() != null) {
                IceCandidate candidate = new IceCandidate(
                        payload.getString("sdpMid"),
                        payload.getInt("sdpMLineIndex"),
                        payload.getString("candidate")
                );
                pc.addIceCandidate(candidate);
            }
        }
    }

    private class PnUserHangupAction implements PnAction{
        public static final String TRIGGER = PnRTCMessage.JSON_HANGUP;
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.d("PnUserHangup","PnUserHangupAction");
            PnPeer peer = peers.get(peerId);
            peer.hangup();
            mRtcListener.onPeerConnectionClosed(peer);
            // Todo: Consider Callback?
        }
    }

    private class PnUserMessageAction implements PnAction{
        public static final String TRIGGER = PnRTCMessage.JSON_USERMSG;
        public void execute(String peerId, JSONObject payload) throws JSONException {
            Log.d("PnUserMessage","AddIceCandidateAction");
            JSONObject msgJson = payload.getJSONObject(PnRTCMessage.JSON_USERMSG);
            PnPeer peer = peers.get(peerId);
            mRtcListener.onMessage(peer, msgJson);
        }
    }

    /**
     * @param userId Your id. Used to tag the message before publishing it to another user.
     * @return
     */
    public static JSONObject generateHangupPacket(String userId){
        JSONObject json = new JSONObject();
        try {
            JSONObject packet = new JSONObject();
            packet.put(PnRTCMessage.JSON_HANGUP, true);
            json.put(PnRTCMessage.JSON_PACKET, packet);
            json.put(PnRTCMessage.JSON_ID, ""); //Todo: session id, unused in js SDK?
            json.put(PnRTCMessage.JSON_NUMBER, userId);
        } catch (JSONException e){
            e.printStackTrace();
        }
        return json;
    }

    /**
     * Static method to generate the proper JSON for a user message. Use this when you don't have
     *   a {@link me.kevingleason.pnwebrtc.PnRTCClient} instantiated. Simply send a publish with the
     *   returned JSONObject to the ID that a user is subscribed to.
     * @param userId Your UserID, needed to tag the message
     * @param message The message you with to send some other user
     * @return JSONObject properly formatted for the PubNub WebRTC API
     */
    public static JSONObject generateUserMessage(String userId, JSONObject message){
        JSONObject json = new JSONObject();
        try {
            JSONObject packet = new JSONObject();
            packet.put(PnRTCMessage.JSON_USERMSG, message);
            json.put(PnRTCMessage.JSON_PACKET, packet);
            json.put(PnRTCMessage.JSON_ID, ""); //Todo: session id, unused in js SDK?
            json.put(PnRTCMessage.JSON_NUMBER, userId);
        } catch (JSONException e){
            e.printStackTrace();
        }
        return json;
    }

    private class PnRTCReceiver extends Callback {

        @Override
        public void connectCallback(String channel, Object message) {
            mRtcListener.onDebug(new PnRTCMessage(((JSONArray) message).toString()));
            mRtcListener.onConnected(channel);
        }

        @Override
        public void successCallback(String channel, Object message) {
            if (!(message instanceof JSONObject)) return; // Ignore if not valid JSON.
            JSONObject jsonMessage = (JSONObject) message;
            mRtcListener.onDebug(new PnRTCMessage(jsonMessage));
            try {
                String peerId     = jsonMessage.getString(PnRTCMessage.JSON_NUMBER);
                JSONObject packet = jsonMessage.getJSONObject(PnRTCMessage.JSON_PACKET);
                PnPeer peer;
                if (!peers.containsKey(peerId)){
                    // Possibly threshold number of allowed users
                    peer = addPeer(peerId);
                    peer.pc.addStream(localMediaStream);
                } else {
                    peer = peers.get(peerId);
                }
                if (peer.getStatus().equals(PnPeer.STATUS_DISCONNECTED)) return; // Do nothing if disconnected.
                if (packet.has(PnRTCMessage.JSON_USERMSG)) {
                    actionMap.get(PnUserMessageAction.TRIGGER).execute(peerId,packet);
                    return;
                }
                if (packet.has(PnRTCMessage.JSON_HANGUP)){
                    actionMap.get(PnUserHangupAction.TRIGGER).execute(peerId,packet);
                    return;
                }
                if (packet.has(PnRTCMessage.JSON_THUMBNAIL)) {
                    return;   // No handler for thumbnail or hangup yet, will be separate controller callback
                }
                if (packet.has(PnRTCMessage.JSON_SDP)) {
                    if(!peer.received) {
                        peer.setReceived(true);
                        mRtcListener.onDebug(new PnRTCMessage("SDP - " + peer.toString()));
                        // Todo: reveivercb(peer);
                    }
                    String type = packet.getString(PnRTCMessage.JSON_TYPE);
                    actionMap.get(type).execute(peerId, packet);
                    return;
                }
                if (packet.has(PnRTCMessage.JSON_ICE)){
                    actionMap.get(AddIceCandidateAction.TRIGGER).execute(peerId,packet);
                    return;
                }
            } catch (JSONException e){
                e.printStackTrace();
            }
        }

        @Override
        public void errorCallback(String channel, PubnubError error) {
            super.errorCallback(channel, error);
        }

    }

}


package me.kevingleason.pnwebrtc;

import com.pubnub.api.Pubnub;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;

import java.util.List;

/**
 *
Main WebRTC Signaling class, holds all functions to set up {@link org.webrtc.PeerConnection}

 *

 * Author:  Kevin Gleason - Boston College '16
 * File:    PnRTCClient.java
 * Date:    7/20/15
 * Use:     PubNub WebRTC Signaling
 * © 2009 - 2015 PubNub, Inc.
 *

 */
public class PnRTCClient {
    private PnSignalingParams pnSignalingParams;
    private Pubnub mPubNub;
    private PnPeerConnectionClient pcClient;
    private String UUID;

    /**
     * Minimal constructor. Requires a valid Pub and Sub key. Get your Pub/Sub keys for free at
     *  https://admin.pubnub.com/#/register and find keys on developer portal.
     * No UUID provided so a random phone number will be generated with this constructor (XXX-XXXX).
     * @param pubKey PubNub Pub Key
     * @param subKey PubNub Sub Key
     */
    public PnRTCClient(String pubKey, String subKey) {
        this.UUID = generateRandomNumber();
        this.mPubNub  = new Pubnub(pubKey, subKey);
        this.mPubNub.setUUID(this.UUID);
        this.pnSignalingParams = PnSignalingParams.defaultInstance();
        this.pcClient = new PnPeerConnectionClient(this.mPubNub, this.pnSignalingParams, new PnRTCListener() {});
    }

    /**
     * Slightly more verbose constructor. Requires a valid Pub and Sub key. Get your Pub/Sub keys for free at
     *  https://admin.pubnub.com/#/register and find keys on developer portal.
     * @param pubKey PubNub Pub Key
     * @param subKey PubNub Sub Key
     * @param UUID Any UUID to be used as a username
     */
    public PnRTCClient(String pubKey, String subKey, String UUID) {
        this.UUID = UUID;
        this.mPubNub  = new Pubnub(pubKey, subKey);
        this.mPubNub.setUUID(this.UUID);
        this.pnSignalingParams = PnSignalingParams.defaultInstance();
        this.pcClient = new PnPeerConnectionClient(this.mPubNub, this.pnSignalingParams, new PnRTCListener() {});
    }

    /**
     * Return the {@link me.kevingleason.pnwebrtc.PnRTCClient} peer connection constraints.
     * @return Peer Connection Constrains
     */
    public MediaConstraints pcConstraints() {
        return pnSignalingParams.pcConstraints;
    }

    /**
     * Return the {@link me.kevingleason.pnwebrtc.PnRTCClient} video constraints.
     * @return Video Constrains
     */
    public MediaConstraints videoConstraints() {
        return pnSignalingParams.videoConstraints;
    }

    /**
     * Return the {@link me.kevingleason.pnwebrtc.PnRTCClient} audio constraints.
     * @return Audio Constrains
     */
    public MediaConstraints audioConstraints() {
        return pnSignalingParams.audioConstraints;
    }

    /**
     * Return the {@link me.kevingleason.pnwebrtc.PnRTCClient} Pubnub instance.
     * @return The PnRTCClient's {@link com.pubnub.api.Pubnub} instance
     */
    public Pubnub getPubNub(){
        return this.mPubNub;
    }

    /**
     * Return the UUID (username) of the {@link me.kevingleason.pnwebrtc.PnRTCClient}. If not
     *   provided by the constructor, a random phone number is generated and can be retrieived
     *   with this method
     * @return The UUID username of the client
     */
    public String getUUID() {
        return UUID;
    }

    /**
     * Set the signaling parameters. This includes {@link org.webrtc.MediaConstraints} for
     *   {@link org.webrtc.PeerConnection}, Video, and Audio, as well as a list of possible
     *   {@link org.webrtc.PeerConnection.IceServer} candidates.
     * @param signalParams Parameters for WebRTC Signaling
     */
    public void setSignalParams(PnSignalingParams signalParams){
        this.pnSignalingParams = signalParams;
    }

    /**
     * Need to attach mediaStream before you can connect.
     * @param mediaStream Not null local media stream
     */
    public void attachLocalMediaStream(MediaStream mediaStream){
        this.pcClient.setLocalMediaStream(mediaStream);
    }

    /**
     * Attach custom listener for callbacks!
     * @param listener The listener which extends PnRTCListener to implement callbacks
     */
    public void attachRTCListener(PnRTCListener listener){
        this.pcClient.setRTCListener(listener);
    }

    /**
     * Set the maximum simultaneous connections allowed
     * @param max Max simultaneous connections
     */
    public void setMaxConnections(int max){
        this.pcClient.MAX_CONNECTIONS = max;
    }

    /**
     * Subscribe to a channel using PubNub to listen for calls.
     * @param channel The channel to listen on, your "phone number"
     */
    public void listenOn(String channel){
        this.pcClient.listenOn(channel);
    }

    /**
     * Connect with another user by their ID.
     * @param userId The user to establish a WebRTC connection with
     */
    public void connect(String userId){
        this.pcClient.connect(userId);
    }

    /**
     * Close a single peer connection. Send a PubNub hangup signal as well
     * @param userId User to close a connection with
     */
    public void closeConnection(String userId){
        this.pcClient.closeConnection(userId);
    }

    /**
     * Close all peer connections. Send a PubNub hangup signal as well.
     */
    public void closeAllConnections(){
        this.pcClient.closeAllConnections();
    }

    /**
     * Send a custom JSONObject user message to a single peer.
     * @param userId user to send a message to
     * @param message the JSON message to pass to a peer.
     */
    public void transmit(String userId, JSONObject message){
        JSONObject usrMsgJson = new JSONObject();
        try {
            usrMsgJson.put(PnRTCMessage.JSON_USERMSG, message);
            this.pcClient.transmitMessage(userId, usrMsgJson);
        } catch (JSONException e){
            e.printStackTrace();
        }
    }

    /**
     * Send a custom JSONObject user message to all peers.
     * @param message the JSON message to pass to all peers.
     */
    public void transmitAll(JSONObject message){
        List<PnPeer> peerList = this.pcClient.getPeers();
        for(PnPeer p : peerList){
            transmit(p.getId(), message);
        }
    }

    private static String generateRandomNumber(){
        String areaCode = String.valueOf((int)(Math.random()*1000));
        String digits   = String.valueOf((int)(Math.random()*10000));
        while (areaCode.length() < 3) areaCode += "0";
        while (digits.length()   < 4) digits   += "0";
        return areaCode + "-" + digits;
    }

    /**
     * Call this method in Activity.onDestroy() to clost all open connections and clean up
     *   instance for garbage collection.
     */
    public void onDestroy() {
        this.pcClient.closeAllConnections();
        this.mPubNub.unsubscribeAll();
    }

}


package org.openestate.io.immoxml.xml;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
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 <immobilie> element.
 *
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "objektkategorie",
    "geo",
    "kontaktperson",
    "weitereAdresse",
    "preise",
    "flaechen",
    "ausstattung",
    "zustandAngaben",
    "infrastruktur",
    "freitexte",
    "anhaenge",
    "verwaltungObjekt",
    "verwaltungTechn",
    "userDefinedSimplefield",
    "userDefinedAnyfield"
})
@XmlRootElement(name = "immobilie")
@Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
public class Immobilie implements Serializable, Cloneable, CopyTo2, Equals2, ToString2
{

    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Objektkategorie objektkategorie;
    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Geo geo;
    @XmlElement(required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Kontaktperson kontaktperson;
    @XmlElement(name = "weitere_adresse")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected List<WeitereAdresse> weitereAdresse;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Preise preise;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Flaechen flaechen;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Ausstattung ausstattung;
    @XmlElement(name = "zustand_angaben")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected ZustandAngaben zustandAngaben;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Infrastruktur infrastruktur;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Freitexte freitexte;
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected Anhaenge anhaenge;
    @XmlElement(name = "verwaltung_objekt")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected VerwaltungObjekt verwaltungObjekt;
    @XmlElement(name = "verwaltung_techn", required = true)
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected VerwaltungTechn verwaltungTechn;
    @XmlElement(name = "user_defined_simplefield")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected List<UserDefinedSimplefield> userDefinedSimplefield;
    @XmlElement(name = "user_defined_anyfield")
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    protected List<UserDefinedAnyfield> userDefinedAnyfield;

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

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

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

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

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

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

    /**
     * Gets the value of the weitereAdresse property.
     *
     *

     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a set method for the weitereAdresse property.
     *
     *

     * For example, to add a new item, do as follows:
     *

     *    getWeitereAdresse().add(newItem);
     *

     *
     *
     *

     * Objects of the following type(s) are allowed in the list
     * {@link WeitereAdresse }
     *
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public List<WeitereAdresse> getWeitereAdresse() {
        if (weitereAdresse == null) {
            weitereAdresse = new ArrayList<WeitereAdresse>();
        }
        return this.weitereAdresse;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * Gets the value of the userDefinedSimplefield property.
     *
     *

     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a set method for the userDefinedSimplefield property.
     *
     *

     * For example, to add a new item, do as follows:
     *

     *    getUserDefinedSimplefield().add(newItem);
     *

     *
     *
     *

     * Objects of the following type(s) are allowed in the list
     * {@link UserDefinedSimplefield }
     *
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public List<UserDefinedSimplefield> getUserDefinedSimplefield() {
        if (userDefinedSimplefield == null) {
            userDefinedSimplefield = new ArrayList<UserDefinedSimplefield>();
        }
        return this.userDefinedSimplefield;
    }

    /**
     * Gets the value of the userDefinedAnyfield property.
     *
     *

     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a set method for the userDefinedAnyfield property.
     *
     *

     * For example, to add a new item, do as follows:
     *

     *    getUserDefinedAnyfield().add(newItem);
     *

     *
     *
     *

     * Objects of the following type(s) are allowed in the list
     * {@link UserDefinedAnyfield }
     *
     *
     */
    @Generated(value = "com.sun.tools.xjc.Driver", date = "2018-10-12T02:50:55+02:00", comments = "JAXB RI v2.2.11")
    public List<UserDefinedAnyfield> getUserDefinedAnyfield() {
        if (userDefinedAnyfield == null) {
            userDefinedAnyfield = new ArrayList<UserDefinedAnyfield>();
        }
        return this.userDefinedAnyfield;
    }

    @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) {
        {
            Objektkategorie theObjektkategorie;
            theObjektkategorie = this.getObjektkategorie();
            strategy.appendField(locator, this, "objektkategorie", buffer, theObjektkategorie, (this.objektkategorie!= null));
        }
        {
            Geo theGeo;
            theGeo = this.getGeo();
            strategy.appendField(locator, this, "geo", buffer, theGeo, (this.geo!= null));
        }
        {
            Kontaktperson theKontaktperson;
            theKontaktperson = this.getKontaktperson();
            strategy.appendField(locator, this, "kontaktperson", buffer, theKontaktperson, (this.kontaktperson!= null));
        }
        {
            List<WeitereAdresse> theWeitereAdresse;
            theWeitereAdresse = (((this.weitereAdresse!= null)&&(!this.weitereAdresse.isEmpty()))?this.getWeitereAdresse():null);
            strategy.appendField(locator, this, "weitereAdresse", buffer, theWeitereAdresse, ((this.weitereAdresse!= null)&&(!this.weitereAdresse.isEmpty())));
        }
        {
            Preise thePreise;
            thePreise = this.getPreise();
            strategy.appendField(locator, this, "preise", buffer, thePreise, (this.preise!= null));
        }
        {
            Flaechen theFlaechen;
            theFlaechen = this.getFlaechen();
            strategy.appendField(locator, this, "flaechen", buffer, theFlaechen, (this.flaechen!= null));
        }
        {
            Ausstattung theAusstattung;
            theAusstattung = this.getAusstattung();
            strategy.appendField(locator, this, "ausstattung", buffer, theAusstattung, (this.ausstattung!= null));
        }
        {
            ZustandAngaben theZustandAngaben;
            theZustandAngaben = this.getZustandAngaben();
            strategy.appendField(locator, this, "zustandAngaben", buffer, theZustandAngaben, (this.zustandAngaben!= null));
        }
        {
            Infrastruktur theInfrastruktur;
            theInfrastruktur = this.getInfrastruktur();
            strategy.appendField(locator, this, "infrastruktur", buffer, theInfrastruktur, (this.infrastruktur!= null));
        }
        {
            Freitexte theFreitexte;
            theFreitexte = this.getFreitexte();
            strategy.appendField(locator, this, "freitexte", buffer, theFreitexte, (this.freitexte!= null));
        }
        {
            Anhaenge theAnhaenge;
            theAnhaenge = this.getAnhaenge();
            strategy.appendField(locator, this, "anhaenge", buffer, theAnhaenge, (this.anhaenge!= null));
        }
        {
            VerwaltungObjekt theVerwaltungObjekt;
            theVerwaltungObjekt = this.getVerwaltungObjekt();
            strategy.appendField(locator, this, "verwaltungObjekt", buffer, theVerwaltungObjekt, (this.verwaltungObjekt!= null));
        }
        {
            VerwaltungTechn theVerwaltungTechn;
            theVerwaltungTechn = this.getVerwaltungTechn();
            strategy.appendField(locator, this, "verwaltungTechn", buffer, theVerwaltungTechn, (this.verwaltungTechn!= null));
        }
        {
            List<UserDefinedSimplefield> theUserDefinedSimplefield;
            theUserDefinedSimplefield = (((this.userDefinedSimplefield!= null)&&(!this.userDefinedSimplefield.isEmpty()))?this.getUserDefinedSimplefield():null);
            strategy.appendField(locator, this, "userDefinedSimplefield", buffer, theUserDefinedSimplefield, ((this.userDefinedSimplefield!= null)&&(!this.userDefinedSimplefield.isEmpty())));
        }
        {
            List<UserDefinedAnyfield> theUserDefinedAnyfield;
            theUserDefinedAnyfield = (((this.userDefinedAnyfield!= null)&&(!this.userDefinedAnyfield.isEmpty()))?this.getUserDefinedAnyfield():null);
            strategy.appendField(locator, this, "userDefinedAnyfield", buffer, theUserDefinedAnyfield, ((this.userDefinedAnyfield!= null)&&(!this.userDefinedAnyfield.isEmpty())));
        }
        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 Immobilie) {
            final Immobilie copy = ((Immobilie) draftCopy);
            {
                Boolean objektkategorieShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.objektkategorie!= null));
                if (objektkategorieShouldBeCopiedAndSet == Boolean.TRUE) {
                    Objektkategorie sourceObjektkategorie;
                    sourceObjektkategorie = this.getObjektkategorie();
                    Objektkategorie copyObjektkategorie = ((Objektkategorie) strategy.copy(LocatorUtils.property(locator, "objektkategorie", sourceObjektkategorie), sourceObjektkategorie, (this.objektkategorie!= null)));
                    copy.setObjektkategorie(copyObjektkategorie);
                } else {
                    if (objektkategorieShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.objektkategorie = null;
                    }
                }
            }
            {
                Boolean geoShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.geo!= null));
                if (geoShouldBeCopiedAndSet == Boolean.TRUE) {
                    Geo sourceGeo;
                    sourceGeo = this.getGeo();
                    Geo copyGeo = ((Geo) strategy.copy(LocatorUtils.property(locator, "geo", sourceGeo), sourceGeo, (this.geo!= null)));
                    copy.setGeo(copyGeo);
                } else {
                    if (geoShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.geo = null;
                    }
                }
            }
            {
                Boolean kontaktpersonShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.kontaktperson!= null));
                if (kontaktpersonShouldBeCopiedAndSet == Boolean.TRUE) {
                    Kontaktperson sourceKontaktperson;
                    sourceKontaktperson = this.getKontaktperson();
                    Kontaktperson copyKontaktperson = ((Kontaktperson) strategy.copy(LocatorUtils.property(locator, "kontaktperson", sourceKontaktperson), sourceKontaktperson, (this.kontaktperson!= null)));
                    copy.setKontaktperson(copyKontaktperson);
                } else {
                    if (kontaktpersonShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.kontaktperson = null;
                    }
                }
            }
            {
                Boolean weitereAdresseShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, ((this.weitereAdresse!= null)&&(!this.weitereAdresse.isEmpty())));
                if (weitereAdresseShouldBeCopiedAndSet == Boolean.TRUE) {
                    List<WeitereAdresse> sourceWeitereAdresse;
                    sourceWeitereAdresse = (((this.weitereAdresse!= null)&&(!this.weitereAdresse.isEmpty()))?this.getWeitereAdresse():null);
                    @SuppressWarnings("unchecked")
                    List<WeitereAdresse> copyWeitereAdresse = ((List<WeitereAdresse> ) strategy.copy(LocatorUtils.property(locator, "weitereAdresse", sourceWeitereAdresse), sourceWeitereAdresse, ((this.weitereAdresse!= null)&&(!this.weitereAdresse.isEmpty()))));
                    copy.weitereAdresse = null;
                    if (copyWeitereAdresse!= null) {
                        List<WeitereAdresse> uniqueWeitereAdressel = copy.getWeitereAdresse();
                        uniqueWeitereAdressel.addAll(copyWeitereAdresse);
                    }
                } else {
                    if (weitereAdresseShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.weitereAdresse = null;
                    }
                }
            }
            {
                Boolean preiseShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.preise!= null));
                if (preiseShouldBeCopiedAndSet == Boolean.TRUE) {
                    Preise sourcePreise;
                    sourcePreise = this.getPreise();
                    Preise copyPreise = ((Preise) strategy.copy(LocatorUtils.property(locator, "preise", sourcePreise), sourcePreise, (this.preise!= null)));
                    copy.setPreise(copyPreise);
                } else {
                    if (preiseShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.preise = null;
                    }
                }
            }
            {
                Boolean flaechenShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.flaechen!= null));
                if (flaechenShouldBeCopiedAndSet == Boolean.TRUE) {
                    Flaechen sourceFlaechen;
                    sourceFlaechen = this.getFlaechen();
                    Flaechen copyFlaechen = ((Flaechen) strategy.copy(LocatorUtils.property(locator, "flaechen", sourceFlaechen), sourceFlaechen, (this.flaechen!= null)));
                    copy.setFlaechen(copyFlaechen);
                } else {
                    if (flaechenShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.flaechen = null;
                    }
                }
            }
            {
                Boolean ausstattungShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.ausstattung!= null));
                if (ausstattungShouldBeCopiedAndSet == Boolean.TRUE) {
                    Ausstattung sourceAusstattung;
                    sourceAusstattung = this.getAusstattung();
                    Ausstattung copyAusstattung = ((Ausstattung) strategy.copy(LocatorUtils.property(locator, "ausstattung", sourceAusstattung), sourceAusstattung, (this.ausstattung!= null)));
                    copy.setAusstattung(copyAusstattung);
                } else {
                    if (ausstattungShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.ausstattung = null;
                    }
                }
            }
            {
                Boolean zustandAngabenShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.zustandAngaben!= null));
                if (zustandAngabenShouldBeCopiedAndSet == Boolean.TRUE) {
                    ZustandAngaben sourceZustandAngaben;
                    sourceZustandAngaben = this.getZustandAngaben();
                    ZustandAngaben copyZustandAngaben = ((ZustandAngaben) strategy.copy(LocatorUtils.property(locator, "zustandAngaben", sourceZustandAngaben), sourceZustandAngaben, (this.zustandAngaben!= null)));
                    copy.setZustandAngaben(copyZustandAngaben);
                } else {
                    if (zustandAngabenShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.zustandAngaben = null;
                    }
                }
            }
            {
                Boolean infrastrukturShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.infrastruktur!= null));
                if (infrastrukturShouldBeCopiedAndSet == Boolean.TRUE) {
                    Infrastruktur sourceInfrastruktur;
                    sourceInfrastruktur = this.getInfrastruktur();
                    Infrastruktur copyInfrastruktur = ((Infrastruktur) strategy.copy(LocatorUtils.property(locator, "infrastruktur", sourceInfrastruktur), sourceInfrastruktur, (this.infrastruktur!= null)));
                    copy.setInfrastruktur(copyInfrastruktur);
                } else {
                    if (infrastrukturShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.infrastruktur = null;
                    }
                }
            }
            {
                Boolean freitexteShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.freitexte!= null));
                if (freitexteShouldBeCopiedAndSet == Boolean.TRUE) {
                    Freitexte sourceFreitexte;
                    sourceFreitexte = this.getFreitexte();
                    Freitexte copyFreitexte = ((Freitexte) strategy.copy(LocatorUtils.property(locator, "freitexte", sourceFreitexte), sourceFreitexte, (this.freitexte!= null)));
                    copy.setFreitexte(copyFreitexte);
                } else {
                    if (freitexteShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.freitexte = null;
                    }
                }
            }
            {
                Boolean anhaengeShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.anhaenge!= null));
                if (anhaengeShouldBeCopiedAndSet == Boolean.TRUE) {
                    Anhaenge sourceAnhaenge;
                    sourceAnhaenge = this.getAnhaenge();
                    Anhaenge copyAnhaenge = ((Anhaenge) strategy.copy(LocatorUtils.property(locator, "anhaenge", sourceAnhaenge), sourceAnhaenge, (this.anhaenge!= null)));
                    copy.setAnhaenge(copyAnhaenge);
                } else {
                    if (anhaengeShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.anhaenge = null;
                    }
                }
            }
            {
                Boolean verwaltungObjektShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.verwaltungObjekt!= null));
                if (verwaltungObjektShouldBeCopiedAndSet == Boolean.TRUE) {
                    VerwaltungObjekt sourceVerwaltungObjekt;
                    sourceVerwaltungObjekt = this.getVerwaltungObjekt();
                    VerwaltungObjekt copyVerwaltungObjekt = ((VerwaltungObjekt) strategy.copy(LocatorUtils.property(locator, "verwaltungObjekt", sourceVerwaltungObjekt), sourceVerwaltungObjekt, (this.verwaltungObjekt!= null)));
                    copy.setVerwaltungObjekt(copyVerwaltungObjekt);
                } else {
                    if (verwaltungObjektShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.verwaltungObjekt = null;
                    }
                }
            }
            {
                Boolean verwaltungTechnShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, (this.verwaltungTechn!= null));
                if (verwaltungTechnShouldBeCopiedAndSet == Boolean.TRUE) {
                    VerwaltungTechn sourceVerwaltungTechn;
                    sourceVerwaltungTechn = this.getVerwaltungTechn();
                    VerwaltungTechn copyVerwaltungTechn = ((VerwaltungTechn) strategy.copy(LocatorUtils.property(locator, "verwaltungTechn", sourceVerwaltungTechn), sourceVerwaltungTechn, (this.verwaltungTechn!= null)));
                    copy.setVerwaltungTechn(copyVerwaltungTechn);
                } else {
                    if (verwaltungTechnShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.verwaltungTechn = null;
                    }
                }
            }
            {
                Boolean userDefinedSimplefieldShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, ((this.userDefinedSimplefield!= null)&&(!this.userDefinedSimplefield.isEmpty())));
                if (userDefinedSimplefieldShouldBeCopiedAndSet == Boolean.TRUE) {
                    List<UserDefinedSimplefield> sourceUserDefinedSimplefield;
                    sourceUserDefinedSimplefield = (((this.userDefinedSimplefield!= null)&&(!this.userDefinedSimplefield.isEmpty()))?this.getUserDefinedSimplefield():null);
                    @SuppressWarnings("unchecked")
                    List<UserDefinedSimplefield> copyUserDefinedSimplefield = ((List<UserDefinedSimplefield> ) strategy.copy(LocatorUtils.property(locator, "userDefinedSimplefield", sourceUserDefinedSimplefield), sourceUserDefinedSimplefield, ((this.userDefinedSimplefield!= null)&&(!this.userDefinedSimplefield.isEmpty()))));
                    copy.userDefinedSimplefield = null;
                    if (copyUserDefinedSimplefield!= null) {
                        List<UserDefinedSimplefield> uniqueUserDefinedSimplefieldl = copy.getUserDefinedSimplefield();
                        uniqueUserDefinedSimplefieldl.addAll(copyUserDefinedSimplefield);
                    }
                } else {
                    if (userDefinedSimplefieldShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.userDefinedSimplefield = null;
                    }
                }
            }
            {
                Boolean userDefinedAnyfieldShouldBeCopiedAndSet = strategy.shouldBeCopiedAndSet(locator, ((this.userDefinedAnyfield!= null)&&(!this.userDefinedAnyfield.isEmpty())));
                if (userDefinedAnyfieldShouldBeCopiedAndSet == Boolean.TRUE) {
                    List<UserDefinedAnyfield> sourceUserDefinedAnyfield;
                    sourceUserDefinedAnyfield = (((this.userDefinedAnyfield!= null)&&(!this.userDefinedAnyfield.isEmpty()))?this.getUserDefinedAnyfield():null);
                    @SuppressWarnings("unchecked")
                    List<UserDefinedAnyfield> copyUserDefinedAnyfield = ((List<UserDefinedAnyfield> ) strategy.copy(LocatorUtils.property(locator, "userDefinedAnyfield", sourceUserDefinedAnyfield), sourceUserDefinedAnyfield, ((this.userDefinedAnyfield!= null)&&(!this.userDefinedAnyfield.isEmpty()))));
                    copy.userDefinedAnyfield = null;
                    if (copyUserDefinedAnyfield!= null) {
                        List<UserDefinedAnyfield> uniqueUserDefinedAnyfieldl = copy.getUserDefinedAnyfield();
                        uniqueUserDefinedAnyfieldl.addAll(copyUserDefinedAnyfield);
                    }
                } else {
                    if (userDefinedAnyfieldShouldBeCopiedAndSet == Boolean.FALSE) {
                        copy.userDefinedAnyfield = 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 Immobilie();
    }

    @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 Immobilie that = ((Immobilie) object);
        {
            Objektkategorie lhsObjektkategorie;
            lhsObjektkategorie = this.getObjektkategorie();
            Objektkategorie rhsObjektkategorie;
            rhsObjektkategorie = that.getObjektkategorie();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "objektkategorie", lhsObjektkategorie), LocatorUtils.property(thatLocator, "objektkategorie", rhsObjektkategorie), lhsObjektkategorie, rhsObjektkategorie, (this.objektkategorie!= null), (that.objektkategorie!= null))) {
                return false;
            }
        }
        {
            Geo lhsGeo;
            lhsGeo = this.getGeo();
            Geo rhsGeo;
            rhsGeo = that.getGeo();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "geo", lhsGeo), LocatorUtils.property(thatLocator, "geo", rhsGeo), lhsGeo, rhsGeo, (this.geo!= null), (that.geo!= null))) {
                return false;
            }
        }
        {
            Kontaktperson lhsKontaktperson;
            lhsKontaktperson = this.getKontaktperson();
            Kontaktperson rhsKontaktperson;
            rhsKontaktperson = that.getKontaktperson();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "kontaktperson", lhsKontaktperson), LocatorUtils.property(thatLocator, "kontaktperson", rhsKontaktperson), lhsKontaktperson, rhsKontaktperson, (this.kontaktperson!= null), (that.kontaktperson!= null))) {
                return false;
            }
        }
        {
            List<WeitereAdresse> lhsWeitereAdresse;
            lhsWeitereAdresse = (((this.weitereAdresse!= null)&&(!this.weitereAdresse.isEmpty()))?this.getWeitereAdresse():null);
            List<WeitereAdresse> rhsWeitereAdresse;
            rhsWeitereAdresse = (((that.weitereAdresse!= null)&&(!that.weitereAdresse.isEmpty()))?that.getWeitereAdresse():null);
            if (!strategy.equals(LocatorUtils.property(thisLocator, "weitereAdresse", lhsWeitereAdresse), LocatorUtils.property(thatLocator, "weitereAdresse", rhsWeitereAdresse), lhsWeitereAdresse, rhsWeitereAdresse, ((this.weitereAdresse!= null)&&(!this.weitereAdresse.isEmpty())), ((that.weitereAdresse!= null)&&(!that.weitereAdresse.isEmpty())))) {
                return false;
            }
        }
        {
            Preise lhsPreise;
            lhsPreise = this.getPreise();
            Preise rhsPreise;
            rhsPreise = that.getPreise();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "preise", lhsPreise), LocatorUtils.property(thatLocator, "preise", rhsPreise), lhsPreise, rhsPreise, (this.preise!= null), (that.preise!= null))) {
                return false;
            }
        }
        {
            Flaechen lhsFlaechen;
            lhsFlaechen = this.getFlaechen();
            Flaechen rhsFlaechen;
            rhsFlaechen = that.getFlaechen();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "flaechen", lhsFlaechen), LocatorUtils.property(thatLocator, "flaechen", rhsFlaechen), lhsFlaechen, rhsFlaechen, (this.flaechen!= null), (that.flaechen!= null))) {
                return false;
            }
        }
        {
            Ausstattung lhsAusstattung;
            lhsAusstattung = this.getAusstattung();
            Ausstattung rhsAusstattung;
            rhsAusstattung = that.getAusstattung();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "ausstattung", lhsAusstattung), LocatorUtils.property(thatLocator, "ausstattung", rhsAusstattung), lhsAusstattung, rhsAusstattung, (this.ausstattung!= null), (that.ausstattung!= null))) {
                return false;
            }
        }
        {
            ZustandAngaben lhsZustandAngaben;
            lhsZustandAngaben = this.getZustandAngaben();
            ZustandAngaben rhsZustandAngaben;
            rhsZustandAngaben = that.getZustandAngaben();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "zustandAngaben", lhsZustandAngaben), LocatorUtils.property(thatLocator, "zustandAngaben", rhsZustandAngaben), lhsZustandAngaben, rhsZustandAngaben, (this.zustandAngaben!= null), (that.zustandAngaben!= null))) {
                return false;
            }
        }
        {
            Infrastruktur lhsInfrastruktur;
            lhsInfrastruktur = this.getInfrastruktur();
            Infrastruktur rhsInfrastruktur;
            rhsInfrastruktur = that.getInfrastruktur();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "infrastruktur", lhsInfrastruktur), LocatorUtils.property(thatLocator, "infrastruktur", rhsInfrastruktur), lhsInfrastruktur, rhsInfrastruktur, (this.infrastruktur!= null), (that.infrastruktur!= null))) {
                return false;
            }
        }
        {
            Freitexte lhsFreitexte;
            lhsFreitexte = this.getFreitexte();
            Freitexte rhsFreitexte;
            rhsFreitexte = that.getFreitexte();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "freitexte", lhsFreitexte), LocatorUtils.property(thatLocator, "freitexte", rhsFreitexte), lhsFreitexte, rhsFreitexte, (this.freitexte!= null), (that.freitexte!= null))) {
                return false;
            }
        }
        {
            Anhaenge lhsAnhaenge;
            lhsAnhaenge = this.getAnhaenge();
            Anhaenge rhsAnhaenge;
            rhsAnhaenge = that.getAnhaenge();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "anhaenge", lhsAnhaenge), LocatorUtils.property(thatLocator, "anhaenge", rhsAnhaenge), lhsAnhaenge, rhsAnhaenge, (this.anhaenge!= null), (that.anhaenge!= null))) {
                return false;
            }
        }
        {
            VerwaltungObjekt lhsVerwaltungObjekt;
            lhsVerwaltungObjekt = this.getVerwaltungObjekt();
            VerwaltungObjekt rhsVerwaltungObjekt;
            rhsVerwaltungObjekt = that.getVerwaltungObjekt();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "verwaltungObjekt", lhsVerwaltungObjekt), LocatorUtils.property(thatLocator, "verwaltungObjekt", rhsVerwaltungObjekt), lhsVerwaltungObjekt, rhsVerwaltungObjekt, (this.verwaltungObjekt!= null), (that.verwaltungObjekt!= null))) {
                return false;
            }
        }
        {
            VerwaltungTechn lhsVerwaltungTechn;
            lhsVerwaltungTechn = this.getVerwaltungTechn();
            VerwaltungTechn rhsVerwaltungTechn;
            rhsVerwaltungTechn = that.getVerwaltungTechn();
            if (!strategy.equals(LocatorUtils.property(thisLocator, "verwaltungTechn", lhsVerwaltungTechn), LocatorUtils.property(thatLocator, "verwaltungTechn", rhsVerwaltungTechn), lhsVerwaltungTechn, rhsVerwaltungTechn, (this.verwaltungTechn!= null), (that.verwaltungTechn!= null))) {
                return false;
            }
        }
        {
            List<UserDefinedSimplefield> lhsUserDefinedSimplefield;
            lhsUserDefinedSimplefield = (((this.userDefinedSimplefield!= null)&&(!this.userDefinedSimplefield.isEmpty()))?this.getUserDefinedSimplefield():null);
            List<UserDefinedSimplefield> rhsUserDefinedSimplefield;
            rhsUserDefinedSimplefield = (((that.userDefinedSimplefield!= null)&&(!that.userDefinedSimplefield.isEmpty()))?that.getUserDefinedSimplefield():null);
            if (!strategy.equals(LocatorUtils.property(thisLocator, "userDefinedSimplefield", lhsUserDefinedSimplefield), LocatorUtils.property(thatLocator, "userDefinedSimplefield", rhsUserDefinedSimplefield), lhsUserDefinedSimplefield, rhsUserDefinedSimplefield, ((this.userDefinedSimplefield!= null)&&(!this.userDefinedSimplefield.isEmpty())), ((that.userDefinedSimplefield!= null)&&(!that.userDefinedSimplefield.isEmpty())))) {
                return false;
            }
        }
        {
            List<UserDefinedAnyfield> lhsUserDefinedAnyfield;
            lhsUserDefinedAnyfield = (((this.userDefinedAnyfield!= null)&&(!this.userDefinedAnyfield.isEmpty()))?this.getUserDefinedAnyfield():null);
            List<UserDefinedAnyfield> rhsUserDefinedAnyfield;
            rhsUserDefinedAnyfield = (((that.userDefinedAnyfield!= null)&&(!that.userDefinedAnyfield.isEmpty()))?that.getUserDefinedAnyfield():null);
            if (!strategy.equals(LocatorUtils.property(thisLocator, "userDefinedAnyfield", lhsUserDefinedAnyfield), LocatorUtils.property(thatLocator, "userDefinedAnyfield", rhsUserDefinedAnyfield), lhsUserDefinedAnyfield, rhsUserDefinedAnyfield, ((this.userDefinedAnyfield!= null)&&(!this.userDefinedAnyfield.isEmpty())), ((that.userDefinedAnyfield!= null)&&(!that.userDefinedAnyfield.isEmpty())))) {
                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);
    }

}