1f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ================================================================================================= 2f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ADOBE SYSTEMS INCORPORATED 3f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// Copyright 2006 Adobe Systems Incorporated 4f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// All Rights Reserved 5f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// 6f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms 7f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// of the Adobe license agreement accompanying it. 8f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ================================================================================================= 9f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 10f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 11f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 12f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpackage com.adobe.xmp.impl; 13f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 14f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Iterator; 15f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 16f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPConst; 17f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPError; 18f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPException; 19f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPMeta; 20f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPMetaFactory; 21f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPUtils; 22f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.impl.xpath.XMPPath; 23f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.impl.xpath.XMPPathParser; 24f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.options.PropertyOptions; 25f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.properties.XMPAliasInfo; 26f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 27f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 28f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 29f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling/** 30f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @since 11.08.2006 31f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 32f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpublic class XMPUtilsImpl implements XMPConst 33f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling{ 34f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 35f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final int UCK_NORMAL = 0; 36f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 37f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final int UCK_SPACE = 1; 38f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 39f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final int UCK_COMMA = 2; 40f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 41f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final int UCK_SEMICOLON = 3; 42f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 43f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final int UCK_QUOTE = 4; 44f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** */ 45f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final int UCK_CONTROL = 5; 46f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 47f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 48f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 49f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Private constructor, as 50f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 51f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private XMPUtilsImpl() 52f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 53f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // EMPTY 54f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 55f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 56f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 57f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 58f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @see XMPUtils#catenateArrayItems(XMPMeta, String, String, String, String, 59f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * boolean) 60f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 61f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param xmp 62f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The XMP object containing the array to be catenated. 63f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param schemaNS 64f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The schema namespace URI for the array. Must not be null or 65f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the empty string. 66f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param arrayName 67f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The name of the array. May be a general path expression, must 68f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * not be null or the empty string. Each item in the array must 69f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * be a simple string value. 70f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param separator 71f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The string to be used to separate the items in the catenated 72f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * string. Defaults to "; ", ASCII semicolon and space 73f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * (U+003B, U+0020). 74f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param quotes 75f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The characters to be used as quotes around array items that 76f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * contain a separator. Defaults to '"' 77f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param allowCommas 78f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Option flag to control the catenation. 79f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns the string containing the catenated array items. 80f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 81f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Forwards the Exceptions from the metadata processing 82f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 83f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling public static String catenateArrayItems(XMPMeta xmp, String schemaNS, String arrayName, 84f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String separator, String quotes, boolean allowCommas) throws XMPException 85f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 86f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertSchemaNS(schemaNS); 87f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertArrayName(arrayName); 88f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertImplementation(xmp); 89f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (separator == null || separator.length() == 0) 90f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 91f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling separator = "; "; 92f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 93f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (quotes == null || quotes.length() == 0) 94f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 95f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling quotes = "\""; 96f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 97f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 98f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPMetaImpl xmpImpl = (XMPMetaImpl) xmp; 99f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode arrayNode = null; 100f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode currItem = null; 101f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 102f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Return an empty result if the array does not exist, 103f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // hurl if it isn't the right form. 104f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName); 105f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling arrayNode = XMPNodeUtils.findNode(xmpImpl.getRoot(), arrayPath, false, null); 106f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (arrayNode == null) 107f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 108f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return ""; 109f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 110f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (!arrayNode.getOptions().isArray() || arrayNode.getOptions().isArrayAlternate()) 111f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 112f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Named property must be non-alternate array", XMPError.BADPARAM); 113f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 114f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 115f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Make sure the separator is OK. 116f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling checkSeparator(separator); 117f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Make sure the open and close quotes are a legitimate pair. 118f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling char openQuote = quotes.charAt(0); 119f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling char closeQuote = checkQuotes(quotes, openQuote); 120f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 121f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Build the result, quoting the array items, adding separators. 122f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Hurl if any item isn't simple. 123f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 124f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling StringBuffer catinatedString = new StringBuffer(); 125f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 126f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = arrayNode.iterateChildren(); it.hasNext();) 127f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 128f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling currItem = (XMPNode) it.next(); 129f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (currItem.getOptions().isCompositeProperty()) 130f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 131f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Array items must be simple", XMPError.BADPARAM); 132f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 133f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String str = applyQuotes(currItem.getValue(), openQuote, closeQuote, allowCommas); 134f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 135f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling catinatedString.append(str); 136f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (it.hasNext()) 137f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 138f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling catinatedString.append(separator); 139f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 140f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 141f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 142f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return catinatedString.toString(); 143f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 144f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 145f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 146f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 147f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * see {@link XMPUtils#separateArrayItems(XMPMeta, String, String, String, 148f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * PropertyOptions, boolean)} 149f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 150f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param xmp 151f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The XMP object containing the array to be updated. 152f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param schemaNS 153f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The schema namespace URI for the array. Must not be null or 154f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the empty string. 155f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param arrayName 156f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The name of the array. May be a general path expression, must 157f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * not be null or the empty string. Each item in the array must 158f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * be a simple string value. 159f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param catedStr 160f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The string to be separated into the array items. 161f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param arrayOptions 162f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Option flags to control the separation. 163f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param preserveCommas 164f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Flag if commas shall be preserved 165f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 166f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 167f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Forwards the Exceptions from the metadata processing 168f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 169f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling public static void separateArrayItems(XMPMeta xmp, String schemaNS, String arrayName, 170f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String catedStr, PropertyOptions arrayOptions, boolean preserveCommas) 171f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws XMPException 172f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 173f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertSchemaNS(schemaNS); 174f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertArrayName(arrayName); 175f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (catedStr == null) 176f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 177f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Parameter must not be null", XMPError.BADPARAM); 178f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 179f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertImplementation(xmp); 180f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPMetaImpl xmpImpl = (XMPMetaImpl) xmp; 181f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 182f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Keep a zero value, has special meaning below. 183f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode arrayNode = separateFindCreateArray(schemaNS, arrayName, arrayOptions, xmpImpl); 184f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 185f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Extract the item values one at a time, until the whole input string is done. 186f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling String itemValue; 187f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int itemStart, itemEnd; 188f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int nextKind = UCK_NORMAL, charKind = UCK_NORMAL; 189f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling char ch = 0, nextChar = 0; 190f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 191f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemEnd = 0; 192f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int endPos = catedStr.length(); 193f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling while (itemEnd < endPos) 194f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 195f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Skip any leading spaces and separation characters. Always skip commas here. 196f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // They can be kept when within a value, but not when alone between values. 197f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (itemStart = itemEnd; itemStart < endPos; itemStart++) 198f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 199f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ch = catedStr.charAt(itemStart); 200f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling charKind = classifyCharacter(ch); 201f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind == UCK_NORMAL || charKind == UCK_QUOTE) 202f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 203f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 204f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 205f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 206f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (itemStart >= endPos) 207f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 208f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 209f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 210f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 211f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind != UCK_QUOTE) 212f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 213f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is not a quoted value. Scan for the end, create an array 214f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // item from the substring. 215f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (itemEnd = itemStart; itemEnd < endPos; itemEnd++) 216f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 217f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ch = catedStr.charAt(itemEnd); 218f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling charKind = classifyCharacter(ch); 219f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 220f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind == UCK_NORMAL || charKind == UCK_QUOTE || 221f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling (charKind == UCK_COMMA && preserveCommas)) 222f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 223f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling continue; 224f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 225f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (charKind != UCK_SPACE) 226f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 227f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 228f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 229f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if ((itemEnd + 1) < endPos) 230f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 231f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ch = catedStr.charAt(itemEnd + 1); 232f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling nextKind = classifyCharacter(ch); 233f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (nextKind == UCK_NORMAL || nextKind == UCK_QUOTE || 234f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling (nextKind == UCK_COMMA && preserveCommas)) 235f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 236f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling continue; 237f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 238f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 239f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 240f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Anything left? 241f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; // Have multiple spaces, or a space followed by a 242f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // separator. 243f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 244f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemValue = catedStr.substring(itemStart, itemEnd); 245f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 246f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 247f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 248f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Accumulate quoted values into a local string, undoubling 249f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // internal quotes that 250f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // match the surrounding quotes. Do not undouble "unmatching" 251f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // quotes. 252f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 253f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling char openQuote = ch; 254f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling char closeQuote = getClosingQuote(openQuote); 255f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 256f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemStart++; // Skip the opening quote; 257f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemValue = ""; 258f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 259f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (itemEnd = itemStart; itemEnd < endPos; itemEnd++) 260f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 261f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ch = catedStr.charAt(itemEnd); 262f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling charKind = classifyCharacter(ch); 263f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 264f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind != UCK_QUOTE || !isSurroundingQuote(ch, openQuote, closeQuote)) 265f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 266f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is not a matching quote, just append it to the 267f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // item value. 268f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemValue += ch; 269f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 270f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 271f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 272f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is a "matching" quote. Is it doubled, or the 273f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // final closing quote? 274f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Tolerate various edge cases like undoubled opening 275f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // (non-closing) quotes, 276f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // or end of input. 277f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 278f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if ((itemEnd + 1) < endPos) 279f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 280f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling nextChar = catedStr.charAt(itemEnd + 1); 281f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling nextKind = classifyCharacter(nextChar); 282f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 283f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 284f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 285f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling nextKind = UCK_SEMICOLON; 286f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling nextChar = 0x3B; 287f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 288f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 289f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (ch == nextChar) 290f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 291f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is doubled, copy it and skip the double. 292f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemValue += ch; 293f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Loop will add in charSize. 294f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemEnd++; 295f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 296f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (!isClosingingQuote(ch, openQuote, closeQuote)) 297f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 298f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is an undoubled, non-closing quote, copy it. 299f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemValue += ch; 300f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 301f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 302f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 303f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // This is an undoubled closing quote, skip it and 304f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // exit the loop. 305f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling itemEnd++; 306f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 307f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 308f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 309f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 310f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 311f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 312f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Add the separated item to the array. 313f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Keep a matching old value in case it had separators. 314f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int foundIndex = -1; 315f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (int oldChild = 1; oldChild <= arrayNode.getChildrenLength(); oldChild++) 316f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 317f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (itemValue.equals(arrayNode.getChild(oldChild).getValue())) 318f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 319f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling foundIndex = oldChild; 320f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 321f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 322f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 323f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 324f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode newItem = null; 325f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (foundIndex < 0) 326f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 327f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling newItem = new XMPNode(ARRAY_ITEM_NAME, itemValue, null); 328f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling arrayNode.addChild(newItem); 329f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 330f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 331f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 332f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 333f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 334f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 335f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Utility to find or create the array used by <code>separateArrayItems()</code>. 336f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param schemaNS a the namespace fo the array 337f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param arrayName the name of the array 338f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param arrayOptions the options for the array if newly created 339f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param xmp the xmp object 340f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns the array node. 341f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException Forwards exceptions 342f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 343f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static XMPNode separateFindCreateArray(String schemaNS, String arrayName, 344f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling PropertyOptions arrayOptions, XMPMetaImpl xmp) throws XMPException 345f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 346f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling arrayOptions = XMPNodeUtils.verifySetOptions(arrayOptions, null); 347f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!arrayOptions.isOnlyArrayOptions()) 348f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 349f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Options can only provide array form", XMPError.BADOPTIONS); 350f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 351f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 352f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Find the array node, make sure it is OK. Move the current children 353f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // aside, to be readded later if kept. 354f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPPath arrayPath = XMPPathParser.expandXPath(schemaNS, arrayName); 355f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode arrayNode = XMPNodeUtils.findNode(xmp.getRoot(), arrayPath, false, null); 356f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (arrayNode != null) 357f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 358f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The array exists, make sure the form is compatible. Zero 359f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // arrayForm means take what exists. 360f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling PropertyOptions arrayForm = arrayNode.getOptions(); 361f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!arrayForm.isArray() || arrayForm.isArrayAlternate()) 362f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 363f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Named property must be non-alternate array", 364f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADXPATH); 365f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 366f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (arrayOptions.equalArrayTypes(arrayForm)) 367f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 368f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Mismatch of specified and existing array form", 369f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADXPATH); // *** Right error? 370f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 371f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 372f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 373f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 374f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The array does not exist, try to create it. 375f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // don't modify the options handed into the method 376f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling arrayNode = XMPNodeUtils.findNode(xmp.getRoot(), arrayPath, true, arrayOptions 377f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling .setArray(true)); 378f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (arrayNode == null) 379f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 380f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Failed to create named array", XMPError.BADXPATH); 381f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 382f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 383f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return arrayNode; 384f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 385f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 386f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 387f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 388f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @see XMPUtils#removeProperties(XMPMeta, String, String, boolean, boolean) 389f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 390f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param xmp 391f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * The XMP object containing the properties to be removed. 392f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 393f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param schemaNS 394f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Optional schema namespace URI for the properties to be 395f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * removed. 396f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 397f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param propName 398f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Optional path expression for the property to be removed. 399f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 400f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param doAllProperties 401f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Option flag to control the deletion: do internal properties in 402f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * addition to external properties. 403f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param includeAliases 404f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Option flag to control the deletion: Include aliases in the 405f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * "named schema" case above. 406f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException If metadata processing fails 407f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 408f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling public static void removeProperties(XMPMeta xmp, String schemaNS, String propName, 409f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean doAllProperties, boolean includeAliases) throws XMPException 410f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 411f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertImplementation(xmp); 412f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPMetaImpl xmpImpl = (XMPMetaImpl) xmp; 413f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 414f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (propName != null && propName.length() > 0) 415f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 416f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Remove just the one indicated property. This might be an alias, 417f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // the named schema might not actually exist. So don't lookup the 418f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // schema node. 419f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 420f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (schemaNS == null || schemaNS.length() == 0) 421f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 422f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Property name requires schema namespace", 423f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADPARAM); 424f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 425f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 426f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPPath expPath = XMPPathParser.expandXPath(schemaNS, propName); 427f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 428f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode propNode = XMPNodeUtils.findNode(xmpImpl.getRoot(), expPath, false, null); 429f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (propNode != null) 430f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 431f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (doAllProperties 432f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling || !Utils.isInternalProperty(expPath.getSegment(XMPPath.STEP_SCHEMA) 433f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling .getName(), expPath.getSegment(XMPPath.STEP_ROOT_PROP).getName())) 434f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 435f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode parent = propNode.getParent(); 436f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling parent.removeChild(propNode); 437f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (parent.getOptions().isSchemaNode() && !parent.hasChildren()) 438f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 439f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // remove empty schema node 440f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling parent.getParent().removeChild(parent); 441f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 442f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 443f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 444f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 445f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 446f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (schemaNS != null && schemaNS.length() > 0) 447f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 448f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 449f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Remove all properties from the named schema. Optionally include 450f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // aliases, in which case 451f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // there might not be an actual schema node. 452f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 453f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // XMP_NodePtrPos schemaPos; 454f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode schemaNode = XMPNodeUtils.findSchemaNode(xmpImpl.getRoot(), schemaNS, false); 455f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (schemaNode != null) 456f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 457f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (removeSchemaChildren(schemaNode, doAllProperties)) 458f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 459f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling xmpImpl.getRoot().removeChild(schemaNode); 460f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 461f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 462f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 463f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (includeAliases) 464f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 465f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // We're removing the aliases also. Look them up by their 466f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // namespace prefix. 467f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // But that takes more code and the extra speed isn't worth it. 468f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Lookup the XMP node 469f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // from the alias, to make sure the actual exists. 470f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 471f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPAliasInfo[] aliases = XMPMetaFactory.getSchemaRegistry().findAliases(schemaNS); 472f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (int i = 0; i < aliases.length; i++) 473f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 474f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPAliasInfo info = aliases[i]; 475f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPPath path = XMPPathParser.expandXPath(info.getNamespace(), info 476f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling .getPropName()); 477f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode actualProp = XMPNodeUtils 478f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling .findNode(xmpImpl.getRoot(), path, false, null); 479f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (actualProp != null) 480f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 481f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode parent = actualProp.getParent(); 482f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling parent.removeChild(actualProp); 483f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 484f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 485f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 486f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 487f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 488f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 489f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Remove all appropriate properties from all schema. In this case 490f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // we don't have to be 491f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // concerned with aliases, they are handled implicitly from the 492f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // actual properties. 493f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = xmpImpl.getRoot().iterateChildren(); it.hasNext();) 494f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 495f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode schema = (XMPNode) it.next(); 496f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (removeSchemaChildren(schema, doAllProperties)) 497f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 498f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling it.remove(); 499f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 500f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 501f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 502f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 503f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 504f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 505f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 506f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @see XMPUtils#appendProperties(XMPMeta, XMPMeta, boolean, boolean) 507f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param source The source XMP object. 508f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param destination The destination XMP object. 509f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param doAllProperties Do internal properties in addition to external properties. 510f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param replaceOldValues Replace the values of existing properties. 511f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param deleteEmptyValues Delete destination values if source property is empty. 512f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException Forwards the Exceptions from the metadata processing 513f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 514f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling public static void appendProperties(XMPMeta source, XMPMeta destination, 515f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean doAllProperties, boolean replaceOldValues, boolean deleteEmptyValues) 516f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throws XMPException 517f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 518f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertImplementation(source); 519f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling ParameterAsserts.assertImplementation(destination); 520f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 521f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPMetaImpl src = (XMPMetaImpl) source; 522f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPMetaImpl dest = (XMPMetaImpl) destination; 523f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 524f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = src.getRoot().iterateChildren(); it.hasNext();) 525f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 526f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode sourceSchema = (XMPNode) it.next(); 527f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 528f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Make sure we have a destination schema node 529f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode destSchema = XMPNodeUtils.findSchemaNode(dest.getRoot(), 530f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceSchema.getName(), false); 531f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean createdSchema = false; 532f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (destSchema == null) 533f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 534f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destSchema = new XMPNode(sourceSchema.getName(), sourceSchema.getValue(), 535f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling new PropertyOptions().setSchemaNode(true)); 536f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling dest.getRoot().addChild(destSchema); 537f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling createdSchema = true; 538f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 539f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 540f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Process the source schema's children. 541f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator ic = sourceSchema.iterateChildren(); ic.hasNext();) 542f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 543f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode sourceProp = (XMPNode) ic.next(); 544f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (doAllProperties 545f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling || !Utils.isInternalProperty(sourceSchema.getName(), sourceProp.getName())) 546f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 547f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendSubtree( 548f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues); 549f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 550f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 551f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 552f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!destSchema.hasChildren() && (createdSchema || deleteEmptyValues)) 553f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 554f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Don't create an empty schema / remove empty schema. 555f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling dest.getRoot().removeChild(destSchema); 556f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 557f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 558f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 559f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 560f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 561f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 562f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Remove all schema children according to the flag 563f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * <code>doAllProperties</code>. Empty schemas are automatically remove 564f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * by <code>XMPNode</code> 565f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 566f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param schemaNode 567f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * a schema node 568f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param doAllProperties 569f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * flag if all properties or only externals shall be removed. 570f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns true if the schema is empty after the operation. 571f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 572f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static boolean removeSchemaChildren(XMPNode schemaNode, boolean doAllProperties) 573f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 574f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = schemaNode.iterateChildren(); it.hasNext();) 575f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 576f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode currProp = (XMPNode) it.next(); 577f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (doAllProperties 578f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling || !Utils.isInternalProperty(schemaNode.getName(), currProp.getName())) 579f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 580f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling it.remove(); 581f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 582f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 583f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 584f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return !schemaNode.hasChildren(); 585f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 586f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 587f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 588f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 589f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @see XMPUtilsImpl#appendProperties(XMPMeta, XMPMeta, boolean, boolean, boolean) 590f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param destXMP The destination XMP object. 591f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param sourceNode the source node 592f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param destParent the parent of the destination node 593f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param replaceOldValues Replace the values of existing properties. 594f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param deleteEmptyValues flag if properties with empty values should be deleted 595f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * in the destination object. 596f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 597f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 598f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static void appendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, 599f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean replaceOldValues, boolean deleteEmptyValues) throws XMPException 600f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 601f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode destNode = XMPNodeUtils.findChildNode(destParent, sourceNode.getName(), false); 602f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 603f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean valueIsEmpty = false; 604f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (deleteEmptyValues) 605f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 606f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling valueIsEmpty = sourceNode.getOptions().isSimple() ? 607f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceNode.getValue() == null || sourceNode.getValue().length() == 0 : 608f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling !sourceNode.hasChildren(); 609f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 610f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 611f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (deleteEmptyValues && valueIsEmpty) 612f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 613f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (destNode != null) 614f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 615f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destParent.removeChild(destNode); 616f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 617f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 618f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (destNode == null) 619f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 620f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The one easy case, the destination does not exist. 621f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destParent.addChild((XMPNode) sourceNode.clone()); 622f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 623f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (replaceOldValues) 624f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 625f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The destination exists and should be replaced. 626f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destXMP.setNode(destNode, sourceNode.getValue(), sourceNode.getOptions(), true); 627f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destParent.removeChild(destNode); 628f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destNode = (XMPNode) sourceNode.clone(); 629f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destParent.addChild(destNode); 630f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 631f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 632f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 633f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The destination exists and is not totally replaced. Structs and 634f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // arrays are merged. 635f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 636f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling PropertyOptions sourceForm = sourceNode.getOptions(); 637f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling PropertyOptions destForm = destNode.getOptions(); 638f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (sourceForm != destForm) 639f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 640f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return; 641f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 642f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (sourceForm.isStruct()) 643f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 644f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // To merge a struct process the fields recursively. E.g. add simple missing fields. 645f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // The recursive call to AppendSubtree will handle deletion for fields with empty 646f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // values. 647f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = sourceNode.iterateChildren(); it.hasNext();) 648f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 649f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode sourceField = (XMPNode) it.next(); 650f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling appendSubtree(destXMP, sourceField, destNode, 651f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling replaceOldValues, deleteEmptyValues); 652f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (deleteEmptyValues && !destNode.hasChildren()) 653f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 654f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destParent.removeChild(destNode); 655f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 656f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 657f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 658f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (sourceForm.isArrayAltText()) 659f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 660f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first. 661f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Make a special check for deletion of empty values. Meaningful in AltText arrays 662f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // because the "xml:lang" qualifier provides unambiguous source/dest correspondence. 663f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = sourceNode.iterateChildren(); it.hasNext();) 664f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 665f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode sourceItem = (XMPNode) it.next(); 666f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!sourceItem.hasQualifier() 667f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling || !XMPConst.XML_LANG.equals(sourceItem.getQualifier(1).getName())) 668f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 669f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling continue; 670f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 671f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 672f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int destIndex = XMPNodeUtils.lookupLanguageItem(destNode, 673f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceItem.getQualifier(1).getValue()); 674f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (deleteEmptyValues && 675f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling (sourceItem.getValue() == null || 676f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceItem.getValue().length() == 0)) 677f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 678f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (destIndex != -1) 679f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 680f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destNode.removeChild(destIndex); 681f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!destNode.hasChildren()) 682f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 683f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destParent.removeChild(destNode); 684f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 685f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 686f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 687f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (destIndex == -1) 688f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 689f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Not replacing, keep the existing item. 690f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!XMPConst.X_DEFAULT.equals(sourceItem.getQualifier(1).getValue()) 691f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling || !destNode.hasChildren()) 692f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 693f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceItem.cloneSubtree(destNode); 694f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 695f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 696f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 697f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode destItem = new XMPNode( 698f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceItem.getName(), 699f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceItem.getValue(), 700f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceItem.getOptions()); 701f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling sourceItem.cloneSubtree(destItem); 702f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destNode.addChild(1, destItem); 703f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 704f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 705f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 706f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 707f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (sourceForm.isArray()) 708f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 709f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Merge other arrays by item values. Don't worry about order or duplicates. Source 710f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // items with empty values do not cause deletion, that conflicts horribly with 711f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // merging. 712f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 713f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator is = sourceNode.iterateChildren(); is.hasNext();) 714f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 715f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode sourceItem = (XMPNode) is.next(); 716f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 717f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean match = false; 718f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator id = destNode.iterateChildren(); id.hasNext();) 719f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 720f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode destItem = (XMPNode) id.next(); 721f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (itemValuesMatch(sourceItem, destItem)) 722f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 723f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling match = true; 724f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 725f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 726f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!match) 727f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 728f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destNode = (XMPNode) sourceItem.clone(); 729f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling destParent.addChild(destNode); 730f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 731f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 732f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 733f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 734f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 735f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 736f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 737f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 738f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Compares two nodes including its children and qualifier. 739f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param leftNode an <code>XMPNode</code> 740f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param rightNode an <code>XMPNode</code> 741f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns true if the nodes are equal, false otherwise. 742f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException Forwards exceptions to the calling method. 743f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 744f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static boolean itemValuesMatch(XMPNode leftNode, XMPNode rightNode) throws XMPException 745f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 746f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling PropertyOptions leftForm = leftNode.getOptions(); 747f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling PropertyOptions rightForm = rightNode.getOptions(); 748f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 749f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (leftForm.equals(rightForm)) 750f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 751f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return false; 752f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 753f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 754f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (leftForm.getOptions() == 0) 755f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 756f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Simple nodes, check the values and xml:lang qualifiers. 757f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!leftNode.getValue().equals(rightNode.getValue())) 758f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 759f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return false; 760f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 761f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (leftNode.getOptions().getHasLanguage() != rightNode.getOptions().getHasLanguage()) 762f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 763f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return false; 764f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 765f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (leftNode.getOptions().getHasLanguage() 766f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling && !leftNode.getQualifier(1).getValue().equals( 767f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling rightNode.getQualifier(1).getValue())) 768f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 769f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return false; 770f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 771f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 772f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (leftForm.isStruct()) 773f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 774f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Struct nodes, see if all fields match, ignoring order. 775f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 776f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (leftNode.getChildrenLength() != rightNode.getChildrenLength()) 777f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 778f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return false; 779f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 780f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 781f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator it = leftNode.iterateChildren(); it.hasNext();) 782f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 783f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode leftField = (XMPNode) it.next(); 784f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode rightField = XMPNodeUtils.findChildNode(rightNode, leftField.getName(), 785f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling false); 786f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (rightField == null || !itemValuesMatch(leftField, rightField)) 787f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 788f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return false; 789f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 790f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 791f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 792f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 793f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 794f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Array nodes, see if the "leftNode" values are present in the 795f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // "rightNode", ignoring order, duplicates, 796f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // and extra values in the rightNode-> The rightNode is the 797f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // destination for AppendProperties. 798f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 799f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling assert leftForm.isArray(); 800f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 801f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator il = leftNode.iterateChildren(); il.hasNext();) 802f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 803f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode leftItem = (XMPNode) il.next(); 804f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 805f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean match = false; 806f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (Iterator ir = rightNode.iterateChildren(); ir.hasNext();) 807f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 808f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPNode rightItem = (XMPNode) ir.next(); 809f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (itemValuesMatch(leftItem, rightItem)) 810f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 811f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling match = true; 812f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 813f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 814f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 815f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!match) 816f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 817f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return false; 818f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 819f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 820f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 821f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return true; // All of the checks passed. 822f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 823f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 824f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 825f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 826f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Make sure the separator is OK. It must be one semicolon surrounded by 827f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * zero or more spaces. Any of the recognized semicolons or spaces are 828f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * allowed. 829f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 830f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param separator 831f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 832f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 833f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static void checkSeparator(String separator) throws XMPException 834f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 835f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean haveSemicolon = false; 836f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (int i = 0; i < separator.length(); i++) 837f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 838f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int charKind = classifyCharacter(separator.charAt(i)); 839f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind == UCK_SEMICOLON) 840f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 841f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (haveSemicolon) 842f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 843f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Separator can have only one semicolon", 844f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADPARAM); 845f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 846f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling haveSemicolon = true; 847f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 848f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (charKind != UCK_SPACE) 849f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 850f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Separator can have only spaces and one semicolon", 851f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling XMPError.BADPARAM); 852f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 853f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 854f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (!haveSemicolon) 855f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 856f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Separator must have one semicolon", XMPError.BADPARAM); 857f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 858f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 859f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 860f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 861f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 862f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Make sure the open and close quotes are a legitimate pair and return the 863f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * correct closing quote or an exception. 864f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 865f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param quotes 866f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * opened and closing quote in a string 867f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param openQuote 868f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the open quote 869f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns a corresponding closing quote. 870f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @throws XMPException 871f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 872f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static char checkQuotes(String quotes, char openQuote) throws XMPException 873f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 874f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling char closeQuote; 875f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 876f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int charKind = classifyCharacter(openQuote); 877f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind != UCK_QUOTE) 878f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 879f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Invalid quoting character", XMPError.BADPARAM); 880f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 881f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 882f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (quotes.length() == 1) 883f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 884f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling closeQuote = openQuote; 885f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 886f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 887f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 888f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling closeQuote = quotes.charAt(1); 889f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling charKind = classifyCharacter(closeQuote); 890f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind != UCK_QUOTE) 891f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 892f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Invalid quoting character", XMPError.BADPARAM); 893f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 894f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 895f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 896f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (closeQuote != getClosingQuote(openQuote)) 897f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 898f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling throw new XMPException("Mismatched quote pair", XMPError.BADPARAM); 899f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 900f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return closeQuote; 901f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 902f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 903f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 904f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 905f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Classifies the character into normal chars, spaces, semicola, quotes, 906f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * control chars. 907f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 908f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param ch 909f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * a char 910f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Return the character kind. 911f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 912f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static int classifyCharacter(char ch) 913f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 914f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (SPACES.indexOf(ch) >= 0 || (0x2000 <= ch && ch <= 0x200B)) 915f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 916f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return UCK_SPACE; 917f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 918f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (COMMAS.indexOf(ch) >= 0) 919f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 920f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return UCK_COMMA; 921f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 922f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (SEMICOLA.indexOf(ch) >= 0) 923f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 924f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return UCK_SEMICOLON; 925f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 926f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (QUOTES.indexOf(ch) >= 0 || (0x3008 <= ch && ch <= 0x300F) 927f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling || (0x2018 <= ch && ch <= 0x201F)) 928f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 929f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return UCK_QUOTE; 930f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 931f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else if (ch < 0x0020 || CONTROLS.indexOf(ch) >= 0) 932f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 933f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return UCK_CONTROL; 934f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 935f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 936f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 937f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Assume typical case. 938f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return UCK_NORMAL; 939f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 940f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 941f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 942f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 943f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 944f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param openQuote 945f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the open quote char 946f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns the matching closing quote for an open quote. 947f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 948f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static char getClosingQuote(char openQuote) 949f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 950f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling switch (openQuote) 951f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 952f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x0022: 953f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x0022; // ! U+0022 is both opening and closing. 954f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x005B: 955f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x005D; 956f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x00AB: 957f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x00BB; // ! U+00AB and U+00BB are reversible. 958f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x00BB: 959f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x00AB; 960f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x2015: 961f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x2015; // ! U+2015 is both opening and closing. 962f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x2018: 963f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x2019; 964f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x201A: 965f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x201B; 966f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x201C: 967f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x201D; 968f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x201E: 969f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x201F; 970f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x2039: 971f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x203A; // ! U+2039 and U+203A are reversible. 972f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x203A: 973f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x2039; 974f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x3008: 975f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x3009; 976f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x300A: 977f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x300B; 978f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x300C: 979f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x300D; 980f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x300E: 981f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x300F; 982f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling case 0x301D: 983f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0x301F; // ! U+301E also closes U+301D. 984f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling default: 985f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return 0; 986f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 987f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 988f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 989f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 990f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 991f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Add quotes to the item. 992f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * 993f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param item 994f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the array item 995f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param openQuote 996f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the open quote character 997f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param closeQuote 998f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * the closing quote character 999f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param allowCommas 1000f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * flag if commas are allowed 1001f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns the value in quotes. 1002f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1003f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static String applyQuotes(String item, char openQuote, char closeQuote, 1004f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean allowCommas) 1005f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1006f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (item == null) 1007f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1008f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling item = ""; 1009f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1010f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1011f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling boolean prevSpace = false; 1012f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int charOffset; 1013f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int charKind; 1014f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1015f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // See if there are any separators in the value. Stop at the first 1016f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // occurrance. This is a bit 1017f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // tricky in order to make typical typing work conveniently. The purpose 1018f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // of applying quotes 1019f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // is to preserve the values when splitting them back apart. That is 1020f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // CatenateContainerItems 1021f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // and SeparateContainerItems must round trip properly. For the most 1022f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // part we only look for 1023f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // separators here. Internal quotes, as in -- Irving "Bud" Jones -- 1024f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // won't cause problems in 1025f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // the separation. An initial quote will though, it will make the value 1026f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // look quoted. 1027f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1028f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int i; 1029f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (i = 0; i < item.length(); i++) 1030f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1031f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling char ch = item.charAt(i); 1032f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling charKind = classifyCharacter(ch); 1033f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (i == 0 && charKind == UCK_QUOTE) 1034f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1035f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 1036f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1037f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1038f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (charKind == UCK_SPACE) 1039f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1040f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Multiple spaces are a separator. 1041f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (prevSpace) 1042f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1043f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 1044f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1045f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling prevSpace = true; 1046f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1047f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling else 1048f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1049f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling prevSpace = false; 1050f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if ((charKind == UCK_SEMICOLON || charKind == UCK_CONTROL) 1051f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling || (charKind == UCK_COMMA && !allowCommas)) 1052f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1053f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 1054f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1055f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1056f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1057f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1058f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1059f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (i < item.length()) 1060f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1061f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Create a quoted copy, doubling any internal quotes that match the 1062f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // outer ones. Internal quotes did not stop the "needs quoting" 1063f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // search, but they do need 1064f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // doubling. So we have to rescan the front of the string for 1065f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // quotes. Handle the special 1066f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // case of U+301D being closed by either U+301E or U+301F. 1067f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1068f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling StringBuffer newItem = new StringBuffer(item.length() + 2); 1069f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling int splitPoint; 1070f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (splitPoint = 0; splitPoint <= i; splitPoint++) 1071f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1072f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (classifyCharacter(item.charAt(i)) == UCK_QUOTE) 1073f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1074f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling break; 1075f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1076f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1077f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1078f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling // Copy the leading "normal" portion. 1079f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling newItem.append(openQuote).append(item.substring(0, splitPoint)); 1080f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1081f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling for (charOffset = splitPoint; charOffset < item.length(); charOffset++) 1082f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1083f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling newItem.append(item.charAt(charOffset)); 1084f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling if (classifyCharacter(item.charAt(charOffset)) == UCK_QUOTE 1085f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling && isSurroundingQuote(item.charAt(charOffset), openQuote, closeQuote)) 1086f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1087f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling newItem.append(item.charAt(charOffset)); 1088f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1089f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1090f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1091f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling newItem.append(closeQuote); 1092f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1093f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling item = newItem.toString(); 1094f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1095f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1096f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return item; 1097f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1098f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1099f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1100f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1101f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param ch a character 1102f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param openQuote the opening quote char 1103f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param closeQuote the closing quote char 1104f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Return it the character is a surrounding quote. 1105f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1106f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static boolean isSurroundingQuote(char ch, char openQuote, char closeQuote) 1107f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1108f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return ch == openQuote || isClosingingQuote(ch, openQuote, closeQuote); 1109f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1110f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1111f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1112f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1113f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param ch a character 1114f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param openQuote the opening quote char 1115f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @param closeQuote the closing quote char 1116f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @return Returns true if the character is a closing quote. 1117f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1118f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static boolean isClosingingQuote(char ch, char openQuote, char closeQuote) 1119f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling { 1120f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling return ch == closeQuote || (openQuote == 0x301D && ch == 0x301E || ch == 0x301F); 1121f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling } 1122f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1123f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1124f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling 1125f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1126f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+0022 ASCII space<br> 1127f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+3000, ideographic space<br> 1128f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+303F, ideographic half fill space<br> 1129f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+2000..U+200B, en quad through zero width space 1130f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1131f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String SPACES = "\u0020\u3000\u303F"; 1132f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1133f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+002C, ASCII comma<br> 1134f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+FF0C, full width comma<br> 1135f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+FF64, half width ideographic comma<br> 1136f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+FE50, small comma<br> 1137f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+FE51, small ideographic comma<br> 1138f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+3001, ideographic comma<br> 1139f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+060C, Arabic comma<br> 1140f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+055D, Armenian comma 1141f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1142f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String COMMAS = "\u002C\uFF0C\uFF64\uFE50\uFE51\u3001\u060C\u055D"; 1143f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1144f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+003B, ASCII semicolon<br> 1145f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+FF1B, full width semicolon<br> 1146f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+FE54, small semicolon<br> 1147f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+061B, Arabic semicolon<br> 1148f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+037E, Greek "semicolon" (really a question mark) 1149f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1150f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String SEMICOLA = "\u003B\uFF1B\uFE54\u061B\u037E"; 1151f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1152f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+0022 ASCII quote<br> 1153f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * ASCII '[' (0x5B) and ']' (0x5D) are used as quotes in Chinese and 1154f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Korean.<br> 1155f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+00AB and U+00BB, guillemet quotes<br> 1156f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+3008..U+300F, various quotes.<br> 1157f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+301D..U+301F, double prime quotes.<br> 1158f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+2015, dash quote.<br> 1159f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+2018..U+201F, various quotes.<br> 1160f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+2039 and U+203A, guillemet quotes. 1161f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1162f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String QUOTES = 1163f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling "\"\u005B\u005D\u00AB\u00BB\u301D\u301E\u301F\u2015\u2039\u203A"; 1164f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling /** 1165f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+0000..U+001F ASCII controls<br> 1166f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+2028, line separator.<br> 1167f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * U+2029, paragraph separator. 1168f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */ 1169f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling private static final String CONTROLS = "\u2028\u2029"; 1170f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling} 1171