/*
* 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 info.ineighborhood.cardme.engine;
import info.ineighborhood.cardme.io.CompatibilityMode;
import info.ineighborhood.cardme.util.Base64Wrapper;
import info.ineighborhood.cardme.util.ISOUtils;
import info.ineighborhood.cardme.util.VCardUtils;
import info.ineighborhood.cardme.vcard.EncodingType;
import info.ineighborhood.cardme.vcard.VCard;
import info.ineighborhood.cardme.vcard.VCardImpl;
import info.ineighborhood.cardme.vcard.VCardType;
import info.ineighborhood.cardme.vcard.VCardVersion;
import info.ineighborhood.cardme.vcard.errors.ErrorSeverity;
import info.ineighborhood.cardme.vcard.errors.VCardBuildException;
import info.ineighborhood.cardme.vcard.errors.VCardError;
import info.ineighborhood.cardme.vcard.errors.VCardException;
import info.ineighborhood.cardme.vcard.features.AddressFeature;
import info.ineighborhood.cardme.vcard.features.BeginFeature;
import info.ineighborhood.cardme.vcard.features.BirthdayFeature;
import info.ineighborhood.cardme.vcard.features.CategoriesFeature;
import info.ineighborhood.cardme.vcard.features.ClassFeature;
import info.ineighborhood.cardme.vcard.features.DisplayableNameFeature;
import info.ineighborhood.cardme.vcard.features.EmailFeature;
import info.ineighborhood.cardme.vcard.features.EndFeature;
import info.ineighborhood.cardme.vcard.features.ExtendedFeature;
import info.ineighborhood.cardme.vcard.features.FormattedNameFeature;
import info.ineighborhood.cardme.vcard.features.GeographicPositionFeature;
import info.ineighborhood.cardme.vcard.features.KeyFeature;
import info.ineighborhood.cardme.vcard.features.LabelFeature;
import info.ineighborhood.cardme.vcard.features.LogoFeature;
import info.ineighborhood.cardme.vcard.features.MailerFeature;
import info.ineighborhood.cardme.vcard.features.NameFeature;
import info.ineighborhood.cardme.vcard.features.NicknameFeature;
import info.ineighborhood.cardme.vcard.features.NoteFeature;
import info.ineighborhood.cardme.vcard.features.OrganizationFeature;
import info.ineighborhood.cardme.vcard.features.PhotoFeature;
import info.ineighborhood.cardme.vcard.features.ProductIdFeature;
import info.ineighborhood.cardme.vcard.features.ProfileFeature;
import info.ineighborhood.cardme.vcard.features.RevisionFeature;
import info.ineighborhood.cardme.vcard.features.RoleFeature;
import info.ineighborhood.cardme.vcard.features.SortStringFeature;
import info.ineighborhood.cardme.vcard.features.SoundFeature;
import info.ineighborhood.cardme.vcard.features.SourceFeature;
import info.ineighborhood.cardme.vcard.features.TelephoneFeature;
import info.ineighborhood.cardme.vcard.features.TimeZoneFeature;
import info.ineighborhood.cardme.vcard.features.TitleFeature;
import info.ineighborhood.cardme.vcard.features.UIDFeature;
import info.ineighborhood.cardme.vcard.features.URLFeature;
import info.ineighborhood.cardme.vcard.features.VersionFeature;
import info.ineighborhood.cardme.vcard.types.AddressType;
import info.ineighborhood.cardme.vcard.types.BeginType;
import info.ineighborhood.cardme.vcard.types.BirthdayType;
import info.ineighborhood.cardme.vcard.types.CategoriesType;
import info.ineighborhood.cardme.vcard.types.ClassType;
import info.ineighborhood.cardme.vcard.types.DisplayableNameType;
import info.ineighborhood.cardme.vcard.types.EmailType;
import info.ineighborhood.cardme.vcard.types.EndType;
import info.ineighborhood.cardme.vcard.types.ExtendedType;
import info.ineighborhood.cardme.vcard.types.FormattedNameType;
import info.ineighborhood.cardme.vcard.types.GeographicPositionType;
import info.ineighborhood.cardme.vcard.types.KeyType;
import info.ineighborhood.cardme.vcard.types.LabelType;
import info.ineighborhood.cardme.vcard.types.LogoType;
import info.ineighborhood.cardme.vcard.types.MailerType;
import info.ineighborhood.cardme.vcard.types.NameType;
import info.ineighborhood.cardme.vcard.types.NicknameType;
import info.ineighborhood.cardme.vcard.types.NoteType;
import info.ineighborhood.cardme.vcard.types.OrganizationType;
import info.ineighborhood.cardme.vcard.types.PhotoType;
import info.ineighborhood.cardme.vcard.types.ProductIdType;
import info.ineighborhood.cardme.vcard.types.ProfileType;
import info.ineighborhood.cardme.vcard.types.RevisionType;
import info.ineighborhood.cardme.vcard.types.RoleType;
import info.ineighborhood.cardme.vcard.types.SortStringType;
import info.ineighborhood.cardme.vcard.types.SoundType;
import info.ineighborhood.cardme.vcard.types.SourceType;
import info.ineighborhood.cardme.vcard.types.TelephoneType;
import info.ineighborhood.cardme.vcard.types.TimeZoneType;
import info.ineighborhood.cardme.vcard.types.TitleType;
import info.ineighborhood.cardme.vcard.types.UIDType;
import info.ineighborhood.cardme.vcard.types.URLType;
import info.ineighborhood.cardme.vcard.types.VersionType;
import info.ineighborhood.cardme.vcard.types.media.AudioMediaType;
import info.ineighborhood.cardme.vcard.types.media.ImageMediaType;
import info.ineighborhood.cardme.vcard.types.media.KeyTextType;
import info.ineighborhood.cardme.vcard.types.parameters.AddressParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.EmailParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.KeyParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.LabelParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.LogoParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.PhotoParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.SoundParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.TelephoneParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.XAddressParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.XEmailParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.XLabelParameterType;
import info.ineighborhood.cardme.vcard.types.parameters.XTelephoneParameterType;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Pattern;
/**
* Copyright (c) 2004, Neighborhood Technologies All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of Neighborhood Technologies nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
*
* @author George El-Haddad <br/> May 6, 2009 - 11:29:02 AM
*
*/
public class VCardEngine {
private static final Pattern VCF_BEGIN_PATTERN = Pattern.compile("\\p{Blank}*((BEGIN)|(begin))\\p{Blank}*:\\p{Blank}*((VCARD)|(vcard))\\p{Blank}*");
private static final Pattern VCF_END_PATTERN = Pattern.compile("\\p{Blank}*((END)|(end))\\p{Blank}*:\\p{Blank}*((VCARD)|(vcard))\\p{Blank}*");
private static final Pattern VCF_GEO_PATTERN = Pattern.compile("-?\\d{1,3}\\.\\d{1,6}\\;-?\\d{1,3}\\.\\d{1,6}");
/**
* <p>Selects from a list of application compatibility modes to use when
* formatting the output of the vcard. Some applications expect a certain
* type of formatting or non-standard types.</p>
*/
private CompatibilityMode compatMode = null;
/**
* <p>Create a VCard parsing engine and initialize it to use RFC2426
* compatibility mode by default.</p>
*/
public VCardEngine() {
compatMode = CompatibilityMode.RFC2426;
}
/**
* <p>Create a VCard parsing engine with a user specified compatibility
* mode.</p>
*
* @param compatMode
*/
public VCardEngine(CompatibilityMode compatMode) {
this.compatMode = compatMode;
}
/**
* <p>Sets a specified compatibility mode.</p>
*
* @see CompatibilityMode
* @param compatMode
*/
public void setCompatibilityMode(CompatibilityMode compatMode) {
if (compatMode == null) {
this.compatMode = CompatibilityMode.RFC2426;
} else {
this.compatMode = compatMode;
}
}
/**
* <p>Returns the currently set compatibility mode. Null if not set.</p>
*
* @return {@link CompatibilityMode}
*/
public CompatibilityMode getCompatibilityMode() {
return compatMode;
}
/**
* <p>Parses the specified VCard file by retrieving the contents of the
* file, unfolding it and then parsing it. The returned result is a VCard
* java object.</p>
*
* @param vcardFile
* @return {@link VCard}
* @throws IOException
*/
public VCard parse(File vcardFile) throws IOException {
String vcardStr = getContentFromFile(vcardFile);
String unfoldedVcardStr = VCardUtils.unfoldVCard(vcardStr);
return parseVCard(unfoldedVcardStr);
}
/**
* <p>Parses the specified VCard String by retrieving the contents of the
* file, unfolding it and then parsing it. The returned result is a VCard
* java object.</p>
*
* @param vcardString
* @return {@link VCard}
* @throws IOException
*/
public VCard parse(String vcardString) throws IOException {
String vcardStr = getContentFromString(vcardString);
String unfoldedVcardStr = VCardUtils.unfoldVCard(vcardStr);
return parseVCard(unfoldedVcardStr);
}
public List<VCard> parseManyInOneVCard(String vcardStr) {
List<VCard> vcards = new ArrayList<VCard>();
List<String> enumCards = enumerateVCards(vcardStr);
for (String card : enumCards) {
VCard vcard = parseVCard(card);
vcards.add(vcard);
}
return vcards;
}
private List<String> enumerateVCards(String vcardString) {
List<String> vcardStrings = new ArrayList<String>();
StringBuilder sb = new StringBuilder();
String[] split = vcardString.split("\r?\n");
boolean begin = false;
boolean end = false;
for (String s : split) {
if (VCF_BEGIN_PATTERN.matcher(s).matches()) {
begin = true;
}
if (VCF_END_PATTERN.matcher(s).matches()) {
end = true;
}
if (begin && !end) {
sb.append(s);
sb.append('\n');
}
if (end) {
sb.append(s);
sb.append('\n');
begin = false;
end = false;
vcardStrings.add(sb.toString());
sb = new StringBuilder();
}
}
return vcardStrings;
}
/**
* <p>Parses the specified VCard String and returns a VCard java object with
* {@link VCardImpl}.setThrowExceptions() set to false. This method assumes
* the following: <ol> <li>The String has only \n end of line markers
* instead of \r\n</li> <li>All lines in the String are unfolded</li> </ol>
* </p>
*
* @param vcardStr
* @return {@link VCard}
*/
private VCard parseVCard(String vcardStr) {
VCardImpl vcard = new VCardImpl();
vcard.setThrowExceptions(false);
List<String[]> arrayLines = splitLines(vcardStr);
String[] vLine = null;
for (int i = 0; i < arrayLines.size(); i++) {
vLine = arrayLines.get(i);
String type = vLine[0].trim(); //VCard Type
String value;
if (vLine.length > 1) {
value = vLine[1].trim(); //VCard Value
} else {
value = null;
}
String paramTypes = null;
String group = null;
if (type.indexOf('.') != -1) {
group = type.substring(0, type.indexOf('.'));
type = type.substring(type.indexOf('.') + 1);
}
if (type.indexOf(';') != -1) {
paramTypes = type.substring(type.indexOf(';') + 1).trim().toUpperCase();
type = type.substring(0, type.indexOf(';')).trim();
}
try {
parseLine(group, type.toUpperCase(), paramTypes, value, vcard);
} catch (VCardBuildException vbe) {
if (vcard.isThrowExceptions()) {
throw new VCardException(vbe.getMessage(), vbe);
} else {
handleError(vcard, vbe.getMessage(), vbe, ErrorSeverity.WARNING);
}
}
}
return vcard;
}
/**
* <p>Parses a specific line of text from the VCard. This line is
* partitioned into the following segments. <ul> <li><b>group</b> Optional
* grouping name for the type.</li> <li><b>type</b> The vcard type that
* identifies this line.</li> <li><b>paramTypes</b> Any parameters that
* follow the type.</li> <li><b>value</b> The actual value or data of the
* type.</li> <li><b>vcard</b> The vcard object to append the type to once
* parsed.</li> </ul> </p>
*
* @param group
* @param type
* @param paramTypes
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseLine(String group, String type, String paramTypes, String value, VCardImpl vcard) throws VCardBuildException {
VCardType vCardType = null;
//Extended Types are a bit special since they only start with X- and end with anything.
if (type.startsWith("X-")) {
vCardType = VCardType.XTENDED;
} else {
try {
//Enums do not like hyphens so replace it with an underscore.
type = type.replaceAll("-", "_");
vCardType = VCardType.valueOf(type);
} catch (IllegalArgumentException iae) {
if (vcard.isThrowExceptions()) {
throw new VCardException(iae.getMessage(), iae);
} else {
handleError(vcard, "Unrecognizable type name \"" + type + "\"", iae, ErrorSeverity.WARNING);
return;
}
}
}
switch (vCardType) {
case BEGIN: {
parseBeginType(group, value, vcard);
break;
}
case END: {
parseEndType(group, value, vcard);
break;
}
case VERSION: {
parseVersionType(group, value, vcard);
break;
}
case FN: {
parseFnType(group, value, vcard);
break;
}
case N: {
parseNType(group, value, vcard);
break;
}
case NICKNAME: {
parseNicknameType(group, value, vcard);
break;
}
case PHOTO: {
parsePhotoType(group, value, paramTypes, vcard);
break;
}
case BDAY: {
parseBDayType(group, value, vcard);
break;
}
case ADR: {
parseAdrType(group, value, paramTypes, vcard);
break;
}
case LABEL: {
parseLabelType(group, value, paramTypes, vcard);
break;
}
case TEL: {
parseTelType(group, value, paramTypes, vcard);
break;
}
case EMAIL: {
parseEmailType(group, value, paramTypes, vcard);
break;
}
case MAILER: {
parseMailerType(group, value, vcard);
break;
}
case TZ: {
parseTzType(group, value, paramTypes, vcard);
break;
}
case GEO: {
parseGeoType(group, value, vcard);
break;
}
case TITLE: {
parseTitleType(group, value, vcard);
break;
}
case ROLE: {
parseRoleType(group, value, vcard);
break;
}
case LOGO: {
parseLogoType(group, value, paramTypes, vcard);
break;
}
case AGENT: {
//TODO parseAgentType(group, value, vcard);
break;
}
case ORG: {
parseOrgType(group, value, vcard);
break;
}
case CATEGORIES: {
parseCategoriesType(group, value, vcard);
break;
}
case NOTE: {
parseNoteType(group, value, vcard);
break;
}
case PRODID: {
parseProdidType(group, value, vcard);
break;
}
case REV: {
parseRevType(group, value, vcard);
break;
}
case SORT_STRING: {
parseSortStringType(group, value, vcard);
break;
}
case SOUND: {
parseSoundType(group, value, paramTypes, vcard);
break;
}
case UID: {
parseUidType(group, value, vcard);
break;
}
case URL: {
parseUrlType(group, value, vcard);
break;
}
case CLASS: {
parseClassType(group, value, vcard);
break;
}
case KEY: {
parseKeyType(group, value, paramTypes, vcard);
break;
}
case XTENDED: {
parseXtendedType(group, value, type, vcard);
break;
}
case NAME: {
parseDisplayableNameType(group, value, vcard);
break;
}
case PROFILE: {
parseProfileType(group, value, vcard);
break;
}
case SOURCE: {
parseSourceType(group, value, vcard);
break;
}
default: {
throw new VCardBuildException("Unhandled VCard type \"" + vCardType.getType() + "\"");
}
}
}
/**
* <p>Parses the BEGIN type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseBeginType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
BeginFeature beginFeature = new BeginType();
if (value.compareToIgnoreCase("VCARD") == 0) {
if (group != null) {
beginFeature.setGroup(group);
}
vcard.setBegin(beginFeature);
} else {
throw new VCardBuildException("Invalid value for \"BEGIN\" type. Must be \"VCARD\"");
}
} catch (Exception ex) {
throw new VCardBuildException("BeginType (" + VCardType.BEGIN.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the END type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseEndType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
EndFeature endFeature = new EndType();
if (value.compareToIgnoreCase("VCARD") == 0) {
if (group != null) {
endFeature.setGroup(group);
}
vcard.setEnd(endFeature);
} else {
throw new VCardException("Invalid value for \"END\" type. Must be \"VCARD\"");
}
} catch (Exception ex) {
throw new VCardBuildException("EndType (" + VCardType.END.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the VERSION type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseVersionType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
VersionFeature versionFeature = new VersionType();
if (value.compareTo(VCardVersion.V3_0.getVersion()) == 0) {
versionFeature.setVersion(VCardVersion.V3_0);
} else if (value.compareTo(VCardVersion.V2_1.getVersion()) == 0) {
versionFeature.setVersion(VCardVersion.V2_1);
} else {
throw new VCardException("Invalid value for \"VERSION\" type. Must be [3.0, 2.1]");
}
if (group != null) {
versionFeature.setGroup(group);
}
vcard.setVersion(versionFeature);
} catch (Exception ex) {
throw new VCardBuildException("VersionType (" + VCardType.VERSION.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the FN type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseFnType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
FormattedNameFeature formattedNameFeature = new FormattedNameType();
if (VCardUtils.needsUnEscaping(value)) {
formattedNameFeature.setFormattedName(VCardUtils.unescapeString(value));
} else {
formattedNameFeature.setFormattedName(value);
}
if (group != null) {
formattedNameFeature.setGroup(group);
}
vcard.setFormattedName(formattedNameFeature);
} catch (Exception ex) {
throw new VCardBuildException("FormattedNameType (" + VCardType.FN.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the N type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseNType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
NameFeature nameFeature = new NameType();
String[] names = value.split(";", 5);
if (names[0] != null) {
if (VCardUtils.needsUnEscaping(names[0])) {
nameFeature.setFamilyName(VCardUtils.unescapeString(names[0]));
} else {
nameFeature.setFamilyName(names[0]);
}
} else {
throw new VCardBuildException("NameType (" + VCardType.FN.getType() + ") The family name field cannot be left empty.");
}
if (names[1] != null) {
if (VCardUtils.needsUnEscaping(names[1])) {
nameFeature.setGivenName(VCardUtils.unescapeString(names[1]));
} else {
nameFeature.setGivenName(names[1]);
}
} else {
throw new VCardBuildException("NameType (" + VCardType.FN.getType() + ") The given name field cannot be left empty.");
}
if (names[2] != null) {
String[] addNames = names[2].split(",");
for (int i = 0; i < addNames.length; i++) {
if (VCardUtils.needsUnEscaping(addNames[i])) {
nameFeature.addAdditionalName(VCardUtils.unescapeString(addNames[i]));
} else {
nameFeature.addAdditionalName(addNames[i]);
}
}
}
if (names[3] != null) {
String[] prefixes = names[3].split(",");
for (int i = 0; i < prefixes.length; i++) {
if (VCardUtils.needsUnEscaping(prefixes[i])) {
nameFeature.addHonorificPrefix(VCardUtils.unescapeString(prefixes[i]));
} else {
nameFeature.addHonorificPrefix(prefixes[i]);
}
}
}
if (names[4] != null) {
String[] suffixes = names[4].split(",");
for (int i = 0; i < suffixes.length; i++) {
if (VCardUtils.needsUnEscaping(suffixes[i])) {
nameFeature.addHonorificSuffix(VCardUtils.unescapeString(suffixes[i]));
} else {
nameFeature.addHonorificSuffix(suffixes[i]);
}
}
}
if (group != null) {
nameFeature.setGroup(group);
}
vcard.setName(nameFeature);
} catch (Exception ex) {
throw new VCardBuildException("NameType (" + VCardType.N.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the Nickname type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseNicknameType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
NicknameFeature nicknameFeature = new NicknameType();
String[] nicknames = value.split(",");
for (int i = 0; i < nicknames.length; i++) {
if (VCardUtils.needsUnEscaping(nicknames[i])) {
nicknameFeature.addNickname(VCardUtils.unescapeString(nicknames[i]));
} else {
nicknameFeature.addNickname(nicknames[i]);
}
}
if (group != null) {
nicknameFeature.setGroup(group);
}
vcard.setNicknames(nicknameFeature);
} catch (Exception ex) {
throw new VCardBuildException("NicknameType (" + VCardType.NICKNAME.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the PHOTO type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parsePhotoType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
PhotoFeature photoFeature = new PhotoType();
String[] params = paramTypes.split(";");
boolean isBinary = false;
//Parse the parameter types
for (int i = 0; i < params.length; i++) {
String[] paramType = null;
switch (getCompatibilityMode()) {
case I_PHONE:
case MAC_ADDRESS_BOOK: {
if (params[i].contains("=")) {
//For proper vcard parameter types
paramType = params[i].trim().split("=");
} else {
//When the parameter types are missing we try to guess what they are.
//We really should not as it breaks RFC rules but some apps do broken exports.
if (params[i].equals(EncodingType.BASE64.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals(EncodingType.BINARY.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals("URI")) {
paramType = new String[]{PhotoParameterType.VALUE.getTypeName(), params[i]};
} else {
//If we are here then we can only assume this is
//a IANA registered image type (or custom type).
paramType = new String[]{PhotoParameterType.TYPE.getTypeName(), params[i]};
}
}
break;
}
case EVOLUTION:
case KDE_ADDRESS_BOOK:
case MS_OUTLOOK:
case RFC2426:
default: {
if (params[i].contains("=")) {
paramType = params[i].trim().split("=");
} else {
/*
* Type special notes: The type can include the type parameter "TYPE" to
* specify the graphic image format type. The TYPE parameter values MUST
* be one of the IANA registered image formats or a non-standard image format.
*/
paramType = new String[]{PhotoParameterType.TYPE.getTypeName(), params[i]};
}
break;
}
}
/*
* ENCODING=b would look like
* paramType[0] = ENCODING
* paramType[1] = b
*/
PhotoParameterType photoParamType = PhotoParameterType.valueOf(paramType[0]);
switch (photoParamType) {
case ENCODING: {
if (paramType[1].compareToIgnoreCase(EncodingType.BINARY.getType()) == 0) {
photoFeature.setEncodingType(EncodingType.BINARY);
isBinary = true;
} else if (paramType[1].compareToIgnoreCase(EncodingType.BASE64.getType()) == 0) {
photoFeature.setEncodingType(EncodingType.BINARY);
isBinary = true;
} else {
throw new VCardBuildException("PhotoType (" + VCardType.PHOTO.getType() + ") Invalid encoding type \"" + paramType[1] + "\"");
}
break;
}
case TYPE: {
ImageMediaType mediaType = null;
try {
mediaType = ImageMediaType.valueOf(paramType[1]);
photoFeature.setImageMediaType(mediaType);
} catch (IllegalArgumentException iae) {
mediaType = ImageMediaType.NON_STANDARD;
mediaType.setTypeName(paramType[1].trim());
mediaType.setIanaRegisteredName(paramType[1].trim());
mediaType.setExtension(paramType[1].trim());
} finally {
photoFeature.setImageMediaType(mediaType);
}
break;
}
case VALUE: {
if (paramType[1].compareToIgnoreCase("URI") == 0) {
photoFeature.setEncodingType(EncodingType.EIGHT_BIT);
isBinary = false;
} else {
throw new VCardBuildException("PhotoType (" + VCardType.PHOTO.getType() + ") Invalid value type \"" + paramType[1] + "\"");
}
break;
}
}
}
if (isBinary) {
byte[] photoBytes = Base64Wrapper.decode(value);
photoFeature.setCompression(true);
photoFeature.setPhoto(photoBytes);
} else {
URI photoUri = new URI(value);
photoFeature.setPhotoURI(photoUri);
}
if (group != null) {
photoFeature.setGroup(group);
}
vcard.addPhoto(photoFeature);
} catch (Exception ex) {
throw new VCardBuildException("PhotoType (" + VCardType.PHOTO.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the BDAY type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseBDayType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
BirthdayFeature birthdayFeature = new BirthdayType();
if (value.matches(ISOUtils.ISO8601_DATE_EXTENDED_REGEX)) {
//Example: 1996-04-15
String[] date = value.split("-");
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(date[0]));
cal.set(Calendar.MONTH, Integer.parseInt(date[1]) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date[2]));
birthdayFeature.setBirthday(cal);
} else if (value.matches(ISOUtils.ISO8601_DATE_BASIC_REGEX)) {
//Example: 19960415
String year = value.substring(0, 4);
String month = value.substring(4, 6);
String day = value.substring(6);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(year));
cal.set(Calendar.MONTH, Integer.parseInt(month) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day));
birthdayFeature.setBirthday(cal);
} else if (value.matches(ISOUtils.ISO8601_UTC_TIME_BASIC_REGEX)) {
//Example: 19960415T231000Z
String year = value.substring(0, 4);
String month = value.substring(4, 6);
String day = value.substring(6, 8);
String hour = value.substring(9, 11);
String minute = value.substring(11, 13);
String seconds = value.substring(13, 15);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(year));
cal.set(Calendar.MONTH, Integer.parseInt(month) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day));
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour));
cal.set(Calendar.MINUTE, Integer.parseInt(minute));
cal.set(Calendar.SECOND, Integer.parseInt(seconds));
birthdayFeature.setBirthday(cal);
} else if (value.matches(ISOUtils.ISO8601_UTC_TIME_EXTENDED_REGEX)) {
//Example: 1996-04-15T23:10:00Z
String[] split = value.toUpperCase().substring(0, value.indexOf('Z')).split("T");
String[] date = split[0].split("-");
String[] time = split[1].split(":");
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(date[0]));
cal.set(Calendar.MONTH, Integer.parseInt(date[1]) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date[2]));
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(time[0]));
cal.set(Calendar.MINUTE, Integer.parseInt(time[1]));
cal.set(Calendar.SECOND, Integer.parseInt(time[2]));
birthdayFeature.setBirthday(cal);
} else if (value.matches(ISOUtils.ISO8601_TIME_EXTENDED_REGEX)) {
//Example: 1996-04-15T23:10:00-06:00
String[] split = value.toUpperCase().split("T");
String[] date = split[0].split("-");
String time = split[1];
//23:10:00-06:00
String hour = time.substring(0, 2);
String minute = time.substring(3, 5);
String seconds = time.substring(6, 8);
String operator = time.substring(8, 9);
String offsHour = time.substring(9, 11);
String offsMinute = time.substring(12);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(date[0]));
cal.set(Calendar.MONTH, Integer.parseInt(date[1]) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date[2]));
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour));
cal.set(Calendar.MINUTE, Integer.parseInt(minute));
cal.set(Calendar.SECOND, Integer.parseInt(seconds));
if (operator.compareTo("-") == 0) {
offsHour = "-" + offsHour;
}
int offsetMillis = Integer.parseInt(offsHour) + (Integer.parseInt(offsMinute) / 10);
offsetMillis = (((offsetMillis * 60) * 60) * 1000);
cal.set(Calendar.ZONE_OFFSET, offsetMillis);
birthdayFeature.setBirthday(cal);
} else {
throw new VCardBuildException("BirthdayType (" + VCardType.BDAY.getType() + ") Birthday value is not a valid ISO-8601 text.");
}
if (group != null) {
birthdayFeature.setGroup(group);
}
vcard.setBirthday(birthdayFeature);
} catch (Exception ex) {
throw new VCardBuildException("BirthdayType (" + VCardType.BDAY.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the ADR type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseAdrType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
AddressFeature addressFeature = new AddressType();
if (paramTypes != null) {
if (paramTypes.indexOf(';') != -1) {
//Parameter List Style
//Example: TYPE=home;TYPE=parcel;TYPE=postal;TYPE=pref
String[] list = paramTypes.split(";");
for (int i = 0; i < list.length; i++) {
String paramType = list[i];
//Only do this to remove the TYPE prefix on certain occasions
String paramValue = null;
if (paramType.toUpperCase().contains("TYPE")) {
paramValue = paramType.substring(paramType.indexOf('=') + 1);
} else {
paramValue = paramType;
}
try {
AddressParameterType addrParamType = AddressParameterType.valueOf(paramValue);
addressFeature.addAddressParameterType(addrParamType);
} catch (IllegalArgumentException iae) {
XAddressParameterType xAddrType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xAddrType = new XAddressParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xAddrType = new XAddressParameterType(paramValue);
}
addressFeature.addExtendedAddressParameterType(xAddrType);
}
}
} else {
String[] list = paramTypes.substring(paramTypes.indexOf('=') + 1).split(",");
for (int i = 0; i < list.length; i++) {
String paramValue = list[i];
try {
AddressParameterType addrParamType = AddressParameterType.valueOf(paramValue);
addressFeature.addAddressParameterType(addrParamType);
} catch (IllegalArgumentException iae) {
XAddressParameterType xAddrType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xAddrType = new XAddressParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xAddrType = new XAddressParameterType(paramValue);
}
addressFeature.addExtendedAddressParameterType(xAddrType);
}
}
}
}
String[] address = value.split(";", 7);
String postOfficeBox = address[0];
String extendedAddress = address[1];
String streetAddress = address[2];
String locality = address[3];
String region = address[4];
String postalCode = address[5];
String countryName = address[6];
if (postOfficeBox != null) {
if (VCardUtils.needsUnEscaping(postOfficeBox)) {
addressFeature.setPostOfficeBox(VCardUtils.unescapeString(postOfficeBox));
} else {
addressFeature.setPostOfficeBox(postOfficeBox);
}
}
if (extendedAddress != null) {
if (VCardUtils.needsUnEscaping(extendedAddress)) {
addressFeature.setExtendedAddress(VCardUtils.unescapeString(extendedAddress));
} else {
addressFeature.setExtendedAddress(extendedAddress);
}
}
if (streetAddress != null) {
if (VCardUtils.needsUnEscaping(streetAddress)) {
addressFeature.setStreetAddress(VCardUtils.unescapeString(streetAddress));
} else {
addressFeature.setStreetAddress(streetAddress);
}
}
if (locality != null) {
if (VCardUtils.needsUnEscaping(locality)) {
addressFeature.setLocality(VCardUtils.unescapeString(locality));
} else {
addressFeature.setLocality(locality);
}
}
if (region != null) {
if (VCardUtils.needsUnEscaping(region)) {
addressFeature.setRegion(VCardUtils.unescapeString(region));
} else {
addressFeature.setRegion(region);
}
}
if (postalCode != null) {
if (VCardUtils.needsUnEscaping(postalCode)) {
addressFeature.setPostalCode(VCardUtils.unescapeString(postalCode));
} else {
addressFeature.setPostalCode(postalCode);
}
}
if (countryName != null) {
if (VCardUtils.needsUnEscaping(countryName)) {
addressFeature.setCountryName(VCardUtils.unescapeString(countryName));
} else {
addressFeature.setCountryName(countryName);
}
}
if (group != null) {
addressFeature.setGroup(group);
}
vcard.addAddress(addressFeature);
} catch (Exception ex) {
throw new VCardBuildException("AddressType (" + VCardType.ADR.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the LABEL type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseLabelType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
LabelFeature labelFeature = new LabelType();
if (paramTypes != null) {
if (paramTypes.indexOf(';') != -1) {
//Parameter List Style
//Example: TYPE=home;TYPE=parcel;TYPE=postal;TYPE=pref
String[] list = paramTypes.split(";");
for (int i = 0; i < list.length; i++) {
String paramType = list[i];
//Only do this to remove the TYPE prefix on certain occasions
String paramValue = null;
if (paramType.toUpperCase().contains("TYPE")) {
paramValue = paramType.substring(paramType.indexOf('=') + 1);
} else {
paramValue = paramType;
}
try {
LabelParameterType labelParamType = LabelParameterType.valueOf(paramValue);
labelFeature.addLabelParameterType(labelParamType);
} catch (IllegalArgumentException iae) {
XLabelParameterType xLabelType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xLabelType = new XLabelParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xLabelType = new XLabelParameterType(paramValue);
}
labelFeature.addExtendedLabelParameterType(xLabelType);
}
}
} else {
//Parameter Value List Style
//Example: TYPE=home,parcel,postal,intl
String[] list = paramTypes.substring(paramTypes.indexOf('=') + 1).split(",");
for (int i = 0; i < list.length; i++) {
String paramValue = list[i];
try {
LabelParameterType labelParamType = LabelParameterType.valueOf(paramValue);
labelFeature.addLabelParameterType(labelParamType);
} catch (IllegalArgumentException iae) {
XLabelParameterType xLabelType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xLabelType = new XLabelParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xLabelType = new XLabelParameterType(paramValue);
}
labelFeature.addExtendedLabelParameterType(xLabelType);
}
}
}
}
if (VCardUtils.needsUnEscaping(value)) {
labelFeature.setLabel(VCardUtils.unescapeString(value));
} else {
labelFeature.setLabel(value);
}
if (group != null) {
labelFeature.setGroup(group);
}
boolean match = false;
Iterator<AddressFeature> addrIter = vcard.getAddresses();
while (addrIter.hasNext() && !match) {
//Get address and all its parameter and extended parameter types
AddressFeature addr = addrIter.next();
List<AddressParameterType> aPrmList = addr.getAddressParameterTypesList();
List<XAddressParameterType> aXPrmList = addr.getExtendedAddressParameterTypesList();
Iterator<LabelParameterType> lPrmIter = labelFeature.getLabelParameterTypes();
Iterator<XLabelParameterType> lXPrmIter = labelFeature.getExtendedLabelParameterTypes();
//See how many address parameter types match each label parameter type
int paramsMatched = 0;
while (lPrmIter.hasNext()) {
LabelParameterType labelParamType = lPrmIter.next();
for (int i = 0; i < aPrmList.size(); i++) {
if (aPrmList.get(i).getType().equals(labelParamType.getType())) {
paramsMatched++;
}
}
}
//See how many extended address parameter types match each extended label parameter type
int xparamsMatched = 0;
while (lXPrmIter.hasNext()) {
XLabelParameterType xlabelParamType = lXPrmIter.next();
for (int i = 0; i < aXPrmList.size(); i++) {
if (aXPrmList.get(i).getType().equals(xlabelParamType.getType())) {
xparamsMatched++;
}
}
}
//If the number of matching parameter types match between the label
//and the address then this label belongs to the respective address.
if (paramsMatched == labelFeature.getLabelParameterSize() && xparamsMatched == labelFeature.getExtendedLabelParameterSize()) {
//Only set the label on the address if it does not already have one
if (!vcard.hasLabel(addr)) {
vcard.setLabel(labelFeature, addr);
} else {
vcard.addError("Label with duplicate parameter tpyes was detected and ignored. Label -> " + labelFeature.toString(), ErrorSeverity.WARNING, new VCardBuildException("Duplicate label"));
}
match = true;
}
}
} catch (Exception ex) {
throw new VCardBuildException("LabelType (" + VCardType.LABEL.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the TEL type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseTelType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
TelephoneFeature telephoneFeature = new TelephoneType();
if (paramTypes != null) {
if (paramTypes.indexOf(';') != -1) {
//Parameter List Style
//Example: TYPE=cell;TYPE=home;TYPE=fax;TYPE=X-SAT-PHONE;TYPE=X-PRIORITY=1
String[] list = paramTypes.split(";");
for (int i = 0; i < list.length; i++) {
String paramType = list[i];
//Only do this to remove the TYPE prefix on certain occasions
String paramValue = null;
if (paramType.toUpperCase().contains("TYPE")) {
paramValue = paramType.substring(paramType.indexOf('=') + 1);
} else {
paramValue = paramType;
}
try {
TelephoneParameterType telephoneParamType = TelephoneParameterType.valueOf(paramValue);
telephoneFeature.addTelephoneParameterType(telephoneParamType);
} catch (IllegalArgumentException iae) {
XTelephoneParameterType xTelType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xTelType = new XTelephoneParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xTelType = new XTelephoneParameterType(paramValue);
}
telephoneFeature.addExtendedTelephoneParameterType(xTelType);
}
}
} else {
//Parameter Value List Style
//Example: TYPE=cell,home,fax,x-priority=1,x-satphone
String[] list = paramTypes.substring(paramTypes.indexOf('=') + 1).split(",");
for (int i = 0; i < list.length; i++) {
String paramValue = list[i];
try {
TelephoneParameterType telephoneParamType = TelephoneParameterType.valueOf(paramValue);
telephoneFeature.addTelephoneParameterType(telephoneParamType);
} catch (IllegalArgumentException iae) {
XTelephoneParameterType xTelType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xTelType = new XTelephoneParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xTelType = new XTelephoneParameterType(paramValue);
}
telephoneFeature.addExtendedTelephoneParameterType(xTelType);
}
}
}
}
if (VCardUtils.needsUnEscaping(value)) {
telephoneFeature.setTelephone(VCardUtils.unescapeString(value));
} else {
telephoneFeature.setTelephone(value);
}
if (group != null) {
telephoneFeature.setGroup(group);
}
vcard.addTelephoneNumber(telephoneFeature);
} catch (Exception ex) {
throw new VCardBuildException("TelephoneType (" + VCardType.TEL.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the EMAIL type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseEmailType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
EmailFeature emailFeature = new EmailType();
if (paramTypes != null) {
if (paramTypes.indexOf(';') != -1) {
//Parameter List Style
//Example: TYPE=pref;TYPE=internet
String[] list = paramTypes.split(";");
for (int i = 0; i < list.length; i++) {
String paramType = list[i];
//Only do this to remove the TYPE prefix on certain occasions
String paramValue = null;
if (paramType.toUpperCase().contains("TYPE")) {
paramValue = paramType.substring(paramType.indexOf('=') + 1);
} else {
paramValue = paramType;
}
try {
EmailParameterType emailParamType = EmailParameterType.valueOf(paramValue);
emailFeature.addEmailParameterType(emailParamType);
} catch (IllegalArgumentException iae) {
XEmailParameterType xEmailType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xEmailType = new XEmailParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xEmailType = new XEmailParameterType(paramValue);
}
emailFeature.addExtendedEmailParameterType(xEmailType);
}
}
} else {
//Parameter Value List Style
//Example: TYPE=pref,internet
String[] list = paramTypes.substring(paramTypes.indexOf('=') + 1).split(",");
for (int i = 0; i < list.length; i++) {
String paramValue = list[i];
try {
EmailParameterType emailParamType = EmailParameterType.valueOf(paramValue);
emailFeature.addEmailParameterType(emailParamType);
} catch (IllegalArgumentException iae) {
XEmailParameterType xEmailType = null;
if (paramValue.indexOf('=') != -1) {
String[] pTmp = paramValue.split("=");
xEmailType = new XEmailParameterType(pTmp[0], pTmp[1]);
pTmp[0] = null;
pTmp[1] = null;
} else {
xEmailType = new XEmailParameterType(paramValue);
}
emailFeature.addExtendedEmailParameterType(xEmailType);
}
}
}
}
emailFeature.setEmail(value);
if (group != null) {
emailFeature.setGroup(group);
}
vcard.addEmail(emailFeature);
} catch (Exception ex) {
throw new VCardBuildException("EmailType (" + VCardType.EMAIL.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the MAILER type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseMailerType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
MailerFeature mailerFeature = new MailerType();
if (VCardUtils.needsUnEscaping(value)) {
mailerFeature.setMailer(VCardUtils.unescapeString(value));
} else {
mailerFeature.setMailer(value);
}
if (group != null) {
mailerFeature.setGroup(group);
}
vcard.setMailer(mailerFeature);
} catch (Exception ex) {
throw new VCardBuildException("MailerType (" + VCardType.MAILER.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the TZ type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseTzType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
TimeZoneFeature timeZoneFeature = new TimeZoneType();
if (paramTypes != null) {
//VALUE=TEXT
//-05:00; EST; Raleigh/North America
String paramValue = paramTypes.substring(paramTypes.indexOf('=') + 1);
if (paramValue.compareToIgnoreCase("TEXT") == 0) {
timeZoneFeature.setTextValue(value);
}
} else {
if (value.matches(ISOUtils.ISO8601_TIMEZONE_BASIC_REGEX)) {
//-500 or -0500
if (value.startsWith("-")) {
String hour = null;
String minute = null;
if (value.length() == 4) {
hour = value.substring(0, 2);
minute = value.substring(2);
} else if (value.length() == 5) {
hour = value.substring(0, 3);
minute = value.substring(3);
} else {
throw new VCardBuildException("TimeZoneType (" + VCardType.TZ.getType() + ") Timezone value is not a valid ISO-8601 text.");
}
int offsetMillis = Integer.parseInt(hour) + (Integer.parseInt(minute) / 10);
offsetMillis = (((offsetMillis * 60) * 60) * 1000);
TimeZone tz = TimeZone.getDefault();
tz.setRawOffset(offsetMillis);
timeZoneFeature.setTimeZone(tz);
} else {
//500 or 0500
String hour = null;
String minute = null;
if (value.length() == 3) {
hour = value.substring(0, 1);
minute = value.substring(1);
} else if (value.length() == 4) {
hour = value.substring(0, 2);
minute = value.substring(2);
} else {
throw new VCardBuildException("TimeZoneType (" + VCardType.TZ.getType() + ") Timezone value is not a valid ISO-8601 text.");
}
int offsetMillis = Integer.parseInt(hour) + (Integer.parseInt(minute) / 10);
offsetMillis = (((offsetMillis * 60) * 60) * 1000);
TimeZone tz = TimeZone.getDefault();
tz.setRawOffset(offsetMillis);
timeZoneFeature.setTimeZone(tz);
}
} else if (value.matches(ISOUtils.ISO8601_TIMEZONE_EXTENDED_REGEX)) {
//-5:00 or -05:00 or 5:00 or 05:00
String[] split = value.split(":");
String hour = split[0];
String minute = split[1];
int offsetMillis = Integer.parseInt(hour) + (Integer.parseInt(minute) / 10);
offsetMillis = (((offsetMillis * 60) * 60) * 1000);
TimeZone tz = TimeZone.getDefault();
tz.setRawOffset(offsetMillis);
timeZoneFeature.setTimeZone(tz);
} else {
throw new VCardBuildException("TimeZoneType (" + VCardType.TZ.getType() + ") Timezone value is not a valid ISO-8601 text.");
}
}
if (group != null) {
timeZoneFeature.setGroup(group);
}
vcard.setTimeZone(timeZoneFeature);
} catch (Exception ex) {
throw new VCardBuildException("TimeZoneType (" + VCardType.TZ.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the GEO type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseGeoType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
GeographicPositionFeature geographicPositionFeature = new GeographicPositionType();
if (value.matches("-?\\d{1,3}\\.\\d{1,6}\\;-?\\d{1,3}\\.\\d{1,6}")) {
String[] geo = value.split(";");
String lat = geo[0];
String lon = geo[1];
geographicPositionFeature.setLatitude(Float.parseFloat(lat));
geographicPositionFeature.setLongitude(Float.parseFloat(lon));
if (group != null) {
geographicPositionFeature.setGroup(group);
}
vcard.setGeographicPosition(geographicPositionFeature);
} else {
throw new VCardBuildException("GeographicPositionType (" + VCardType.GEO.getType() + ") GeographicPositionType is not valid.");
}
} catch (Exception ex) {
throw new VCardBuildException("GeographicPositionType (" + VCardType.GEO.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the TITLE type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseTitleType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
TitleFeature titleFeature = new TitleType();
if (VCardUtils.needsUnEscaping(value)) {
titleFeature.setTitle(VCardUtils.unescapeString(value));
} else {
titleFeature.setTitle(value);
}
if (group != null) {
titleFeature.setGroup(group);
}
vcard.setTitle(titleFeature);
} catch (Exception ex) {
throw new VCardBuildException("TitleType (" + VCardType.TITLE.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the ROLE type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseRoleType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
RoleFeature roleFeature = new RoleType();
if (VCardUtils.needsUnEscaping(value)) {
roleFeature.setRole(VCardUtils.unescapeString(value));
} else {
roleFeature.setRole(value);
}
if (group != null) {
roleFeature.setGroup(group);
}
vcard.setRole(roleFeature);
} catch (Exception ex) {
throw new VCardBuildException("RoleType (" + VCardType.ROLE.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the LOGO type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseLogoType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
LogoFeature logoFeature = new LogoType();
String[] params = paramTypes.split(";");
boolean isBinary = false;
//Parse the parameter types
for (int i = 0; i < params.length; i++) {
String[] paramType = null;
switch (getCompatibilityMode()) {
case I_PHONE:
case MAC_ADDRESS_BOOK: {
if (params[i].contains("=")) {
//For proper vcard parameter types
paramType = params[i].trim().split("=");
} else {
//When the parameter types are missing we try to guess what they are.
//We really should not as it breaks RFC rules but some apps do broken exports.
if (params[i].equals(EncodingType.BASE64.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals(EncodingType.BINARY.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals("URI")) {
paramType = new String[]{PhotoParameterType.VALUE.getTypeName(), params[i]};
} else {
//If we are here then we can only assume this is
//a IANA registered image type (or custom type).
paramType = new String[]{PhotoParameterType.TYPE.getTypeName(), params[i]};
}
}
break;
}
case EVOLUTION:
case KDE_ADDRESS_BOOK:
case MS_OUTLOOK:
case RFC2426:
default: {
if (params[i].contains("=")) {
paramType = params[i].trim().split("=");
} else {
/*
* Type special notes: The type can include the type parameter "TYPE" to
* specify the graphic image format type. The TYPE parameter values MUST
* be one of the IANA registered image formats or a non-standard image format.
*/
paramType = new String[]{PhotoParameterType.TYPE.getTypeName(), params[i]};
}
break;
}
}
/*
* ENCODING=b would look like
* paramType[0] = ENCODING
* paramType[1] = b
*/
LogoParameterType logoParamType = LogoParameterType.valueOf(paramType[0]);
switch (logoParamType) {
case ENCODING: {
if (paramType[1].compareToIgnoreCase(EncodingType.BINARY.getType()) == 0) {
logoFeature.setEncodingType(EncodingType.BINARY);
isBinary = true;
} else if (paramType[1].compareToIgnoreCase(EncodingType.BASE64.getType()) == 0) {
logoFeature.setEncodingType(EncodingType.BINARY);
isBinary = true;
} else {
throw new VCardBuildException("LogoType (" + VCardType.LOGO.getType() + ") Invalid encoding type \"" + paramType[1] + "\"");
}
break;
}
case TYPE: {
ImageMediaType mediaType = null;
try {
mediaType = ImageMediaType.valueOf(paramType[1]);
logoFeature.setImageMediaType(mediaType);
} catch (IllegalArgumentException iae) {
mediaType = ImageMediaType.NON_STANDARD;
mediaType.setTypeName(paramType[1].trim());
mediaType.setIanaRegisteredName(paramType[1].trim());
mediaType.setExtension(paramType[1].trim());
} finally {
logoFeature.setImageMediaType(mediaType);
}
break;
}
case VALUE: {
if (paramType[1].compareToIgnoreCase("URI") == 0) {
logoFeature.setEncodingType(EncodingType.EIGHT_BIT);
isBinary = false;
} else {
throw new VCardBuildException("LogoType (" + VCardType.LOGO.getType() + ") Invalid value type \"" + paramType[1] + "\"");
}
break;
}
}
}
if (isBinary) {
byte[] logoBytes = Base64Wrapper.decode(value);
logoFeature.setCompression(true);
logoFeature.setLogo(logoBytes);
} else {
URI logoUri = new URI(value);
logoFeature.setLogoURI(logoUri);
}
if (group != null) {
logoFeature.setGroup(group);
}
vcard.addLogo(logoFeature);
} catch (Exception ex) {
throw new VCardBuildException("LogoType (" + VCardType.LOGO.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the ORG type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseOrgType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
OrganizationFeature organizationFeature = new OrganizationType();
/*
* If escaped semi-colons exist in the list then replace them
* with this temporary sequence. Then after splitting put it
* back in.
*/
if (value.contains("\\;")) {
value = value.replaceAll("\\\\;", "!SEMI!");
}
String[] orgs = value.split(";");
for (int i = 0; i < orgs.length; i++) {
if (VCardUtils.needsUnEscaping(orgs[i])) {
String unesc = VCardUtils.unescapeString(orgs[i]);
organizationFeature.addOrganization(unesc.replaceAll("\\!SEMI\\!", ";"));
} else {
organizationFeature.addOrganization(orgs[i].replaceAll("\\!SEMI\\!", ";"));
}
}
if (group != null) {
organizationFeature.setGroup(group);
}
vcard.setOrganizations(organizationFeature);
} catch (Exception ex) {
throw new VCardBuildException("OrganizationType (" + VCardType.ORG.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the CATEGORIES type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseCategoriesType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
CategoriesFeature categoriesFeature = new CategoriesType();
String[] categories = null;
switch (compatMode) {
case KDE_ADDRESS_BOOK: {
if (VCardUtils.needsUnEscaping(value)) {
categories = VCardUtils.unescapeString(value).split(",");
} else {
categories = value.split(",");
}
break;
}
default: {
if (value.contains("\\,")) {
value = value.replaceAll("\\\\,", " ");
}
categories = value.split(",");
break;
}
}
for (int i = 0; i < categories.length; i++) {
if (VCardUtils.needsUnEscaping(categories[i])) {
categoriesFeature.addCategory(VCardUtils.unescapeString(categories[i]));
} else {
categoriesFeature.addCategory(categories[i]);
}
}
if (group != null) {
categoriesFeature.setGroup(group);
}
vcard.setCategories(categoriesFeature);
} catch (Exception ex) {
throw new VCardBuildException("CategoriesType (" + VCardType.CATEGORIES.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the NOTE type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseNoteType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
NoteFeature noteFeature = new NoteType();
if (VCardUtils.needsUnEscaping(value)) {
noteFeature.setNote(VCardUtils.unescapeString(value));
} else {
noteFeature.setNote(value);
}
if (group != null) {
noteFeature.setGroup(group);
}
vcard.addNote(noteFeature);
} catch (Exception ex) {
throw new VCardBuildException("NoteType (" + VCardType.NOTE.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the PRODID type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseProdidType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
ProductIdFeature productIdFeature = new ProductIdType();
productIdFeature.setProductId(value);
if (group != null) {
productIdFeature.setGroup(group);
}
vcard.setProductId(productIdFeature);
} catch (Exception ex) {
throw new VCardBuildException("ProductIdType (" + VCardType.PRODID.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the REV type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseRevType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
RevisionFeature revisionFeature = new RevisionType();
if (value.matches(ISOUtils.ISO8601_DATE_EXTENDED_REGEX)) {
//Example: 1996-04-15
String[] date = value.split("-");
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(date[0]));
cal.set(Calendar.MONTH, Integer.parseInt(date[1]) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date[2]));
revisionFeature.setRevision(cal);
} else if (value.matches(ISOUtils.ISO8601_DATE_BASIC_REGEX)) {
//Example: 19960415
String year = value.substring(0, 4);
String month = value.substring(4, 6);
String day = value.substring(6);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(year));
cal.set(Calendar.MONTH, Integer.parseInt(month) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day));
revisionFeature.setRevision(cal);
} else if (value.matches(ISOUtils.ISO8601_UTC_TIME_BASIC_REGEX)) {
//Example: 19960415T231000Z
String year = value.substring(0, 4);
String month = value.substring(4, 6);
String day = value.substring(6, 8);
String hour = value.substring(9, 11);
String minute = value.substring(11, 13);
String seconds = value.substring(13, 15);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(year));
cal.set(Calendar.MONTH, Integer.parseInt(month) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day));
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour));
cal.set(Calendar.MINUTE, Integer.parseInt(minute));
cal.set(Calendar.SECOND, Integer.parseInt(seconds));
revisionFeature.setRevision(cal);
} else if (value.matches(ISOUtils.ISO8601_UTC_TIME_EXTENDED_REGEX)) {
//Example: 1996-04-15T23:10:00Z
String[] split = value.toUpperCase().substring(0, value.indexOf('Z')).split("T");
String[] date = split[0].split("-");
String[] time = split[1].split(":");
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(date[0]));
cal.set(Calendar.MONTH, Integer.parseInt(date[1]) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date[2]));
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(time[0]));
cal.set(Calendar.MINUTE, Integer.parseInt(time[1]));
cal.set(Calendar.SECOND, Integer.parseInt(time[2]));
revisionFeature.setRevision(cal);
} else if (value.matches(ISOUtils.ISO8601_TIME_EXTENDED_REGEX)) {
//Example: 1996-04-15T23:10:00-06:00
String[] split = value.toUpperCase().split("T");
String[] date = split[0].split("-");
String time = split[1];
//23:10:00-06:00
String hour = time.substring(0, 2);
String minute = time.substring(3, 5);
String seconds = time.substring(6, 8);
String operator = time.substring(8, 9);
String offsHour = time.substring(9, 11);
String offsMinute = time.substring(12);
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(Calendar.YEAR, Integer.parseInt(date[0]));
cal.set(Calendar.MONTH, Integer.parseInt(date[1]) - 1);
cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date[2]));
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour));
cal.set(Calendar.MINUTE, Integer.parseInt(minute));
cal.set(Calendar.SECOND, Integer.parseInt(seconds));
if (operator.compareTo("-") == 0) {
offsHour = "-" + offsHour;
}
int offsetMillis = Integer.parseInt(offsHour) + (Integer.parseInt(offsMinute) / 10);
offsetMillis = (((offsetMillis * 60) * 60) * 1000);
cal.set(Calendar.ZONE_OFFSET, offsetMillis);
revisionFeature.setRevision(cal);
} else {
throw new VCardBuildException("RevisionType (" + VCardType.REV.getType() + ") Revision value is not a valid ISO-8601 text.");
}
if (group != null) {
revisionFeature.setGroup(group);
}
vcard.setRevision(revisionFeature);
} catch (Exception ex) {
throw new VCardBuildException("RevisionType (" + VCardType.REV.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the SORT-STRING type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseSortStringType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
SortStringFeature sortStringFeature = new SortStringType();
sortStringFeature.setSortString(value);
if (group != null) {
sortStringFeature.setGroup(group);
}
vcard.setSortString(sortStringFeature);
} catch (Exception ex) {
throw new VCardBuildException("SortStringType (" + VCardType.SORT_STRING.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the SOUND type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseSoundType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
SoundFeature soundFeature = new SoundType();
String[] params = paramTypes.split(";");
boolean isBinary = false;
//Parse the parameter types
for (int i = 0; i < params.length; i++) {
String[] paramType = null;
switch (getCompatibilityMode()) {
case I_PHONE:
case MAC_ADDRESS_BOOK: {
if (params[i].contains("=")) {
//For proper vcard parameter types
paramType = params[i].trim().split("=");
} else {
//When the parameter types are missing we try to guess what they are.
//We really should not as it breaks RFC rules but some apps do broken exports.
if (params[i].equals(EncodingType.BASE64.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals(EncodingType.BINARY.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals("URI")) {
paramType = new String[]{PhotoParameterType.VALUE.getTypeName(), params[i]};
} else {
//If we are here then we can only assume this is
//a IANA registered image type (or custom type).
paramType = new String[]{PhotoParameterType.TYPE.getTypeName(), params[i]};
}
}
break;
}
case EVOLUTION:
case KDE_ADDRESS_BOOK:
case MS_OUTLOOK:
case RFC2426:
default: {
if (params[i].contains("=")) {
paramType = params[i].trim().split("=");
} else {
/*
* Type special notes: The type can include the type parameter "TYPE" to
* specify the audio format type. The TYPE parameter values MUST be one
* of the IANA registered audio formats or a non-standard audio format.
*/
paramType = new String[]{PhotoParameterType.TYPE.getTypeName(), params[i]};
}
break;
}
}
/*
* ENCODING=b would look like
* paramType[0] = ENCODING
* paramType[1] = b
*/
SoundParameterType soundParamType = SoundParameterType.valueOf(paramType[0]);
switch (soundParamType) {
case ENCODING: {
if (paramType[1].compareToIgnoreCase(EncodingType.BINARY.getType()) == 0) {
soundFeature.setEncodingType(EncodingType.BINARY);
isBinary = true;
} else if (paramType[1].compareToIgnoreCase(EncodingType.BASE64.getType()) == 0) {
soundFeature.setEncodingType(EncodingType.BINARY);
isBinary = true;
} else {
throw new VCardBuildException("SoundType (" + VCardType.SOUND.getType() + ") Invalid encoding type \"" + paramType[1] + "\"");
}
break;
}
case TYPE: {
AudioMediaType mediaType = null;
try {
mediaType = AudioMediaType.valueOf(paramType[1]);
soundFeature.setAudioMediaType(mediaType);
} catch (IllegalArgumentException iae) {
mediaType = AudioMediaType.NON_STANDARD;
mediaType.setTypeName(paramType[1].trim());
mediaType.setIanaRegisteredName(paramType[1].trim());
mediaType.setExtension(paramType[1].trim());
} finally {
soundFeature.setAudioMediaType(mediaType);
}
break;
}
case VALUE: {
if (paramType[1].compareToIgnoreCase("URI") == 0) {
soundFeature.setEncodingType(EncodingType.EIGHT_BIT);
isBinary = false;
} else {
throw new VCardBuildException("SoundType (" + VCardType.SOUND.getType() + ") Invalid value type \"" + paramType[1] + "\"");
}
break;
}
}
}
if (isBinary) {
byte[] soundBytes = Base64Wrapper.decode(value);
soundFeature.setCompression(true);
soundFeature.setSound(soundBytes);
} else {
URI soundUri = new URI(value);
soundFeature.setSoundURI(soundUri);
}
if (group != null) {
soundFeature.setGroup(group);
}
vcard.addSound(soundFeature);
} catch (Exception ex) {
throw new VCardBuildException("SoundType (" + VCardType.SOUND.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the UID type.</p>
*
* @param
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseUidType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
UIDFeature uidFeature = new UIDType();
uidFeature.setUID(value);
if (group != null) {
uidFeature.setGroup(group);
}
vcard.setUID(uidFeature);
} catch (Exception ex) {
throw new VCardBuildException("UIDType (" + VCardType.UID.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the URL type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseUrlType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
URLFeature urlFeature = new URLType();
if (VCardUtils.needsUnEscaping(value)) {
urlFeature.setURL(new URL(VCardUtils.unescapeString(value)));
} else {
urlFeature.setURL(new URL(value));
}
if (group != null) {
urlFeature.setGroup(group);
}
vcard.addURL(urlFeature);
} catch (Exception ex) {
throw new VCardBuildException("URLType (" + VCardType.URL.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the CLASS type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseClassType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
ClassFeature classFeature = new ClassType();
classFeature.setSecurityClass(value);
if (group != null) {
classFeature.setGroup(group);
}
vcard.setSecurityClass(classFeature);
} catch (Exception ex) {
throw new VCardBuildException("ClassType (" + VCardType.CLASS.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the KEY type.</p>
*
* @param group
* @param value
* @param paramTypes
* @param vcard
* @throws VCardBuildException
*/
private void parseKeyType(String group, String value, String paramTypes, VCardImpl vcard) throws VCardBuildException {
try {
KeyFeature keyFeature = new KeyType();
String[] params = paramTypes.split(";");
//Parse the parameter types
for (int i = 0; i < params.length; i++) {
String[] paramType = null;
switch (getCompatibilityMode()) {
case I_PHONE:
case MAC_ADDRESS_BOOK: {
if (params[i].contains("=")) {
//For proper vcard parameter types
paramType = params[i].trim().split("=");
} else {
//When the parameter types are missing we try to guess what they are.
//We really should not as it breaks RFC rules but some apps do broken exports.
if (params[i].equals(EncodingType.BASE64.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals(EncodingType.BINARY.getType())) {
paramType = new String[]{PhotoParameterType.ENCODING.getTypeName(), params[i]};
} else if (params[i].equals("URI")) {
paramType = new String[]{PhotoParameterType.VALUE.getTypeName(), params[i]};
} else {
//If we are here then we can only assume this is
//a IANA registered image type (or custom type).
paramType = new String[]{PhotoParameterType.TYPE.getTypeName(), params[i]};
}
}
break;
}
case EVOLUTION:
case KDE_ADDRESS_BOOK:
case MS_OUTLOOK:
case RFC2426:
default: {
if (params[i].contains("=")) {
paramType = params[i].trim().split("=");
} else {
/*
* Type special notes: The type can also include the type parameter TYPE
* to specify the public key or authentication certificate format. The
* parameter type should specify an IANA registered public key or
* authentication certificate format. The parameter type can also
* specify a non-standard format.
*/
paramType = new String[]{KeyParameterType.TYPE.getTypeName(), params[i]};
}
break;
}
}
/*
* ENCODING=b would look like
* paramType[0] = ENCODING
* paramType[1] = b
*/
KeyParameterType keyParamType = KeyParameterType.valueOf(paramType[0]);
switch (keyParamType) {
case ENCODING: {
if (paramType[1].compareToIgnoreCase(EncodingType.BINARY.getType()) == 0) {
keyFeature.setEncodingType(EncodingType.BINARY);
} else if (paramType[1].compareToIgnoreCase(EncodingType.BASE64.getType()) == 0) {
keyFeature.setEncodingType(EncodingType.BINARY);
} else {
throw new VCardBuildException("KeyType (" + VCardType.KEY.getType() + ") Invalid encoding type \"" + paramType[1] + "\"");
}
break;
}
case TYPE: {
KeyTextType keyTextType = null;
try {
keyTextType = KeyTextType.valueOf(paramType[1]);
keyFeature.setKeyTextType(keyTextType);
} catch (IllegalArgumentException iae) {
keyTextType = KeyTextType.NON_STANDARD;
keyTextType.setTypeName(paramType[1].trim());
keyTextType.setIanaRegisteredName(paramType[1].trim());
keyTextType.setExtension(paramType[1].trim());
} finally {
keyFeature.setKeyTextType(keyTextType);
}
break;
}
}
}
byte[] keyBytes = Base64Wrapper.decode(value);
keyFeature.setCompression(true);
keyFeature.setKey(keyBytes);
if (group != null) {
keyFeature.setGroup(group);
}
vcard.addKey(keyFeature);
} catch (Exception ex) {
throw new VCardBuildException("KeyType (" + VCardType.KEY.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the EXTENDED type.</p>
*
* @param group
* @param value
* @param typeName
* @param vcard
* @throws VCardBuildException
*/
private void parseXtendedType(String group, String value, String typeName, VCardImpl vcard) throws VCardBuildException {
try {
ExtendedFeature extendedFeature = new ExtendedType();
extendedFeature.setExtensionName(typeName);
extendedFeature.setExtensionData(value);
if (group != null) {
extendedFeature.setGroup(group);
}
vcard.addExtendedType(extendedFeature);
} catch (Exception ex) {
throw new VCardBuildException("ExtendedType (" + VCardType.XTENDED.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the NAME type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseDisplayableNameType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
DisplayableNameFeature displayableNameFeature = new DisplayableNameType();
displayableNameFeature.setName(value);
if (group != null) {
displayableNameFeature.setGroup(group);
}
vcard.setDisplayableNameFeature(displayableNameFeature);
} catch (Exception ex) {
throw new VCardBuildException("DisplayableNameType (" + VCardType.NAME.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the PROFILE type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseProfileType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
ProfileFeature profileFeature = new ProfileType();
profileFeature.setProfile(value);
if (group != null) {
profileFeature.setGroup(group);
}
vcard.setProfile(profileFeature);
} catch (Exception ex) {
throw new VCardBuildException("ProfileType (" + VCardType.PROFILE.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Parses the SOURCE type.</p>
*
* @param group
* @param value
* @param vcard
* @throws VCardBuildException
*/
private void parseSourceType(String group, String value, VCardImpl vcard) throws VCardBuildException {
try {
SourceFeature sourceFeature = new SourceType();
sourceFeature.setSource(value);
if (group != null) {
sourceFeature.setGroup(group);
}
vcard.setSource(sourceFeature);
} catch (Exception ex) {
throw new VCardBuildException("SourceType (" + VCardType.SOURCE.getType() + ") [" + ex.getClass().getName() + "] " + ex.getMessage(), ex);
}
}
/**
* <p>Creates a VCardError object and sets the specified error information
* in it and adds it to the VCard currently being parses.</p>
*
* @see VCardError
* @see ErrorSeverity
*
* @param vcard
* @param errorMessage
* @param exception
* @param severity
*/
private void handleError(VCardImpl vcard, String errorMessage, Throwable exception, ErrorSeverity severity) {
VCardError vError = new VCardError();
vError.setErrorMessage(errorMessage);
vError.setSeverity(severity);
if (exception != null) {
vError.setError(exception);
}
vcard.addError(vError);
}
/**
* <p>Splits each line of the vcard into an array of 2 cells. The first cell
* contains the VCard type (may or may not include parameter types.) The
* second cell contains the Type values (may or may not contains comma or
* semicolon delimited lists.)</p>
*
* @param vcardString
* @return List<String[]>
*/
private List<String[]> splitLines(String vcardString) {
String[] strArray = vcardString.split("\n");
List<String[]> arrayLines = new ArrayList<String[]>(strArray.length);
String line = null;
for (int i = 0; i < strArray.length; i++) {
line = strArray[i];
String[] subLine = line.split(":", 2);
arrayLines.add(subLine);
}
return arrayLines;
}
/**
* <p>Returns the contents of the vcard file where each line is delimited
* with the standard java EOL char '\n' This is still a folded vcard
* String.</p>
*
* @param vcardFile
* @return {@link String}
* @throws IOException
*/
private String getContentFromFile(File vcardFile) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(vcardFile));
String line = "";
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
// skips blank lines
if (!line.matches("$")) {
sb.append(line);
sb.append("\n");
}
}
br.close();
return sb.toString();
}
/**
* <p>Returns the contents of the vcard String where each line is delimited
* with the standard java EOL char '\n' This is still a folded vcard
* String.</p>
*
* @param vcardString
* @return {@link String}
* @throws IOException
*/
private String getContentFromString(String vcardString) throws IOException {
BufferedReader br = new BufferedReader(new StringReader(vcardString));
String line = "";
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
// skips blank lines
if (!line.matches("$")) {
sb.append(line);
sb.append("\n");
}
}
br.close();
return sb.toString();
}
}