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

it.cnr.iit.jscontact.tools.dto.Address Maven / Gradle / Ivy

Go to download

Java tools for JSContact building, validation, serialization/deserialization and conversion from vCard 4.0 (RFC6350) and its transliterations, namely xCard (RFC6351) and jCard (RFC7095).

There is a newer version: 1.0.1
Show newest version
/*
 *    Copyright (C) 2020  Consiglio Nazionale delle Ricerche
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Affero General Public License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with this program.  If not, see .
 */
package it.cnr.iit.jscontact.tools.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import it.cnr.iit.jscontact.tools.constraints.BooleanMapConstraint;
import it.cnr.iit.jscontact.tools.constraints.ComponentsConstraint;
import it.cnr.iit.jscontact.tools.constraints.NotNullAnyConstraint;
import it.cnr.iit.jscontact.tools.constraints.NotNullDependencyConstraint;
import it.cnr.iit.jscontact.tools.dto.annotations.ContainsExtensibleEnum;
import it.cnr.iit.jscontact.tools.dto.annotations.JSContactCollection;
import it.cnr.iit.jscontact.tools.dto.deserializers.AddressContextsDeserializer;
import it.cnr.iit.jscontact.tools.dto.deserializers.PhoneticSystemDeserializer;
import it.cnr.iit.jscontact.tools.dto.interfaces.HasComponents;
import it.cnr.iit.jscontact.tools.dto.interfaces.IdMapValue;
import it.cnr.iit.jscontact.tools.dto.interfaces.IsIANAType;
import it.cnr.iit.jscontact.tools.dto.serializers.AddressContextsSerializer;
import lombok.*;
import lombok.experimental.SuperBuilder;
import org.apache.commons.lang3.ArrayUtils;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.util.*;

/**
 * Class mapping the Address type as defined in section 2.5.1 of [RFC9553].
 *
 * @see Section 2.5.1 of RFC9553
 * @author Mario Loffredo
 */
@NotNullAnyConstraint(fieldNames = {"full", "components","coordinates","countryCode", "timeZone"}, message = "at least one not null member between full, components, coordinates, countryCode and timeZone is required in Address")
@NotNullDependencyConstraint(fieldName="components", dependingFieldNames = {"defaultSeparator"})
@ComponentsConstraint
@JsonPropertyOrder({"@type","components","isOrdered","defaultSeparator","full","countryCode","coordinates","timeZone","phoneticScript","phoneticSystem",
                     "contexts","pref"})
@JsonInclude(JsonInclude.Include.NON_NULL)
@SuperBuilder
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of={"hash"}, callSuper = false)
public class Address extends AbstractJSContactType implements IdMapValue, HasComponents, IsIANAType, Serializable {

    @Pattern(regexp = "Address", message="invalid @type value in Address")
    @JsonProperty("@type")
    @Builder.Default
    String _type = "Address";

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @JSContactCollection(addMethod = "addComponent", itemClass = AddressComponent.class)
    AddressComponent[] components;

    Boolean isOrdered;

    String defaultSeparator;

    String full;

    @Pattern(regexp="[a-zA-Z]{2}", message = "invalid countryCode in Address")
    String countryCode;

    @Pattern(regexp="geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?$", message = "invalid coordinates in Address")
    String coordinates;

    String timeZone;

    @Pattern(regexp="[a-zA-Z]{4}", message = "invalid phoneticScript in Address")
    String phoneticScript;

    @JsonDeserialize(using = PhoneticSystemDeserializer.class)
    PhoneticSystem phoneticSystem;

    @JsonSerialize(using = AddressContextsSerializer.class)
    @JsonDeserialize(using = AddressContextsDeserializer.class)
    @BooleanMapConstraint(message = "invalid Map contexts in Address - Only Boolean.TRUE allowed")
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @ContainsExtensibleEnum(enumClass = AddressContextEnum.class, getMethod = "getContexts")
    Map contexts;

    @Min(value=1, message = "invalid pref in Address - value must be greater or equal than 1")
    @Max(value=100, message = "invalid pref in Address - value must be less or equal than 100")
    Integer pref;

    @JsonIgnore
    String altid;

    @JsonIgnore
    String language;

    @JsonIgnore
    String group;

    @JsonIgnore
    String hash;

    private boolean asContext(AddressContext context) { return contexts != null && contexts.containsKey(context); }
    /**
     * Tests if this address is a work address.
     *
     * @return true if the context map includes the "work" context, false otherwise
     */
    public boolean asWork() { return asContext(AddressContext.work()); }
    /**
     * Tests if this address is a private address.
     *
     * @return true if the context map includes the "private" context, false otherwise
     */
    public boolean asPrivate() { return asContext(AddressContext.private_()); }
    /**
     * Tests if this address is a billing address.
     *
     * @return true if the context map includes the "billing" context, false otherwise
     */
    public boolean asBilling() {
        return asContext(AddressContext.billing());
    }

    /**
     * Tests if this address is a delivery address.
     *
     * @return true if the context map includes the "delivery" context, false otherwise
     */
    public boolean asDelivery() {
        return asContext(AddressContext.delivery());
    }

    /**
     * Tests if this address is used in a custom context.
     *
     * @param extValue the custom context in text format
     * @return true if the context map includes the given custom context, false otherwise
     */
    public boolean asExtContext(String extValue) {
        return asContext(AddressContext.ext(extValue));
    }


    /**
     * Tests if the context of this address is not empty.
     *
     * @return true if the context map is not empty, false otherwise
     */
    public boolean hasContext() { return contexts != null && contexts.isEmpty(); }

    private List getStreetAddressDetails(List componentsToCheck) {
        if (components == null)
            return null;

        List addressComponents = new ArrayList<>();
        for (AddressComponentEnum componentEnum : componentsToCheck) {
            AddressComponent component = getComponent(AddressComponentKind.builder().rfcValue(componentEnum).build());
            if (component!=null)
                addressComponents.add(component.getValue());
        }

        return (addressComponents.isEmpty()) ? null : addressComponents;
    }

    /**
     * Returns the street details of this object.
     *
     * @return a text obtained by concatenating the values of AddressComponent items in the "components" array tagged as NUMBER, NAME, BLOCK, SUBDISTRICT, DISTRICT, LANDMARK,  or DIRECTION.
     */
    @JsonIgnore
    public List getStreetAddressItems() {

        return getStreetAddressDetails(Arrays.asList(AddressComponentEnum.NUMBER,
                                                     AddressComponentEnum.NAME,
                                                     AddressComponentEnum.BLOCK,
                                                     AddressComponentEnum.SUBDISTRICT,
                                                     AddressComponentEnum.DISTRICT,
                                                     AddressComponentEnum.LANDMARK,
                                                     AddressComponentEnum.DIRECTION
                ));
    }

    /**
     * Returns the first street address item of this object.
     *
     * @return the first street address item
     */
    @JsonIgnore
    public String getStreetAddress() {
        return (getStreetAddressItems()!=null) ? getStreetAddressItems().get(0) : null;
    }

    /**
     * Returns the street extensions of this object.
     *
     * @return the list of values of the AddressComponent items in the "components" array tagged as BUILDING, FLOOR, APARTMENT, ROOM.
     */
    @JsonIgnore
    public List getStreetExtendedAddressItems() {

        return getStreetAddressDetails(Arrays.asList(AddressComponentEnum.ROOM,
                AddressComponentEnum.APARTMENT,
                AddressComponentEnum.FLOOR,
                AddressComponentEnum.BUILDING));
    }


    /**
     * Returns the first street extension item of this object.
     *
     * @return the first street extension item
     */
    @JsonIgnore
    public String getStreetExtendedAddress() {
        return (getStreetExtendedAddressItems()!=null) ? getStreetExtendedAddressItems().get(0) : null;
    }

    /**
     * Adds a street component to this object.
     *
     * @param sc the street component
     * @param components the street components
     * @return the street components in input plus the sc component
     */
    public static AddressComponent[] addComponent(AddressComponent[] components, AddressComponent sc) {
        return ArrayUtils.add(components, sc);
    }

    /**
     * Adds a street component to this object.
     *
     * @param sc the street component
     */
    public void addComponent(AddressComponent sc) {
        components = addComponent(components, sc);
    }

    /**
     * Adds a context to this object.
     *
     * @param context the context
     */
    public void addContext(AddressContext context) {

        Map clone;
        if (getContexts() == null)
            clone = new HashMap<>();
        else
            clone = new HashMap<>(getContexts());
        clone.put(context,Boolean.TRUE);
        setContexts(clone);
    }

    /**
     * This method will be used to get the extended contexts in the "contexts" property.
     *
     * @return the extended contexts in the "contexts" property
     */
    @JsonIgnore
    public AddressContext[] getExtContexts() {
        if (getContexts() == null)
            return null;
        AddressContext[] extended = null;
        for(AddressContext context : getContexts().keySet()){
            if (context.isExtValue())
                extended = ArrayUtils.add(extended, context);
        }

        return extended;
    }

    private AddressComponent getComponent(AddressComponentKind componentKind) {

        if (components == null)
            return null;

        for (AddressComponent component : components) {
            if (component.getKind().equals(componentKind))
                return component;
        }

        return null;

    }

    private String getComponentValue(AddressComponentKind componentKind) {

        AddressComponent component = getComponent(componentKind);
        return (component != null) ? component.getValue() : null;
    }

    /**
     * Returns the locality of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "locality"
     */
    @JsonIgnore
    public String getLocality() {
        return getComponentValue(AddressComponentKind.locality());
    }

    /**
     * Returns the country of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "country"
     */
    @JsonIgnore
    public String getCountry() {
        return getComponentValue(AddressComponentKind.country());
    }

    /**
     * Returns the region of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "region"
     */
    @JsonIgnore
    public String getRegion() {
        return getComponentValue(AddressComponentKind.region());
    }

    /**
     * Returns the postal code of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "postcode"
     */
    @JsonIgnore
    public String getPostcode() {
        return getComponentValue(AddressComponentKind.postcode());
    }

    /**
     * Returns the P.O. box of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as postOfficeBox
     */
    @JsonIgnore
    public String getPostOfficeBox() {
        return getComponentValue(AddressComponentKind.postOfficeBox());
    }

    /**
     * Returns the apartment of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "apartment"
     */
    @JsonIgnore
    public String getApartment() { return getComponentValue(AddressComponentKind.apartment()); }

    /**
     * Returns the building of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "building"
     */
    @JsonIgnore
    public String getBuilding() { return getComponentValue(AddressComponentKind.building()); }

    /**
     * Returns the floor of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "floor"
     */
    @JsonIgnore
    public String getFloor() { return getComponentValue(AddressComponentKind.floor()); }

    /**
     * Returns the room of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "room"
     */
    @JsonIgnore
    public String getRoom() { return getComponentValue(AddressComponentKind.room()); }

    /**
     * Returns the street name of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "name"
     */
    @JsonIgnore
    public String getStreetName() { return getComponentValue(AddressComponentKind.name()); }

    /**
     * Returns the street number of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "number"
     */
    @JsonIgnore
    public String getStreetNumber() { return getComponentValue(AddressComponentKind.number()); }

    /**
     * Returns the street direction of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "direction"
     */
    @JsonIgnore
    public String getDirection() { return getComponentValue(AddressComponentKind.direction()); }

    /**
     * Returns the district of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "district"
     */
    @JsonIgnore
    public String getDistrict() { return getComponentValue(AddressComponentKind.district()); }

    /**
     * Returns the subdistrict of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "subdistrict"
     */
    @JsonIgnore
    public String getSubDistrict() { return getComponentValue(AddressComponentKind.subdistrict()); }

    /**
     * Returns the block of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "block"
     */
    @JsonIgnore
    public String getBlock() { return getComponentValue(AddressComponentKind.block()); }

    /**
     * Returns the landmark of this object.
     *
     * @return the value of AddressComponent item in the "components" array tagged as "landmark"
     */
    @JsonIgnore
    public String getLandmark() { return getComponentValue(AddressComponentKind.landmark()); }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy