/* * * * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION * * 1. Definitions. * * "License" shall mean the terms and conditions for use, reproduction, * and distribution as defined by Sections 1 through 9 of this document. * * "Licensor" shall mean the copyright owner or entity authorized by * the copyright owner that is granting the License. * * "Legal Entity" shall mean the union of the acting entity and all * other entities that control, are controlled by, or are under common * control with that entity. For the purposes of this definition, * "control" means (i) the power, direct or indirect, to cause the * direction or management of such entity, whether by contract or * otherwise, or (ii) ownership of fifty percent (50%) or more of the * outstanding shares, or (iii) beneficial ownership of such entity. * * "You" (or "Your") shall mean an individual or Legal Entity * exercising permissions granted by this License. * * "Source" form shall mean the preferred form for making modifications, * including but not limited to software source code, documentation * source, and configuration files. * * "Object" form shall mean any form resulting from mechanical * transformation or translation of a Source form, including but * not limited to compiled object code, generated documentation, * and conversions to other media types. * * "Work" shall mean the work of authorship, whether in Source or * Object form, made available under the License, as indicated by a * copyright notice that is included in or attached to the work * (an example is provided in the Appendix below). * * "Derivative Works" shall mean any work, whether in Source or Object * form, that is based on (or derived from) the Work and for which the * editorial revisions, annotations, elaborations, or other modifications * represent, as a whole, an original work of authorship. For the purposes * of this License, Derivative Works shall not include works that remain * separable from, or merely link (or bind by name) to the interfaces of, * the Work and Derivative Works thereof. * * "Contribution" shall mean any work of authorship, including * the original version of the Work and any modifications or additions * to that Work or Derivative Works thereof, that is intentionally * submitted to Licensor for inclusion in the Work by the copyright owner * or by an individual or Legal Entity authorized to submit on behalf of * the copyright owner. For the purposes of this definition, "submitted" * means any form of electronic, verbal, or written communication sent * to the Licensor or its representatives, including but not limited to * communication on electronic mailing lists, source code control systems, * and issue tracking systems that are managed by, or on behalf of, the * Licensor for the purpose of discussing and improving the Work, but * excluding communication that is conspicuously marked or otherwise * designated in writing by the copyright owner as "Not a Contribution." * * "Contributor" shall mean Licensor and any individual or Legal Entity * on behalf of whom a Contribution has been received by Licensor and * subsequently incorporated within the Work. * * 2. Grant of Copyright License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * copyright license to reproduce, prepare Derivative Works of, * publicly display, publicly perform, sublicense, and distribute the * Work and such Derivative Works in Source or Object form. * * 3. Grant of Patent License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * (except as stated in this section) patent license to make, have made, * use, offer to sell, sell, import, and otherwise transfer the Work, * where such license applies only to those patent claims licensable * by such Contributor that are necessarily infringed by their * Contribution(s) alone or by combination of their Contribution(s) * with the Work to which such Contribution(s) was submitted. If You * institute patent litigation against any entity (including a * cross-claim or counterclaim in a lawsuit) alleging that the Work * or a Contribution incorporated within the Work constitutes direct * or contributory patent infringement, then any patent licenses * granted to You under this License for that Work shall terminate * as of the date such litigation is filed. * * 4. Redistribution. You may reproduce and distribute copies of the * Work or Derivative Works thereof in any medium, with or without * modifications, and in Source or Object form, provided that You * meet the following conditions: * * (a) You must give any other recipients of the Work or * Derivative Works a copy of this License; and * * (b) You must cause any modified files to carry prominent notices * stating that You changed the files; and * * (c) You must retain, in the Source form of any Derivative Works * that You distribute, all copyright, patent, trademark, and * attribution notices from the Source form of the Work, * excluding those notices that do not pertain to any part of * the Derivative Works; and * * (d) If the Work includes a "NOTICE" text file as part of its * distribution, then any Derivative Works that You distribute must * include a readable copy of the attribution notices contained * within such NOTICE file, excluding those notices that do not * pertain to any part of the Derivative Works, in at least one * of the following places: within a NOTICE text file distributed * as part of the Derivative Works; within the Source form or * documentation, if provided along with the Derivative Works; or, * within a display generated by the Derivative Works, if and * wherever such third-party notices normally appear. The contents * of the NOTICE file are for informational purposes only and * do not modify the License. You may add Your own attribution * notices within Derivative Works that You distribute, alongside * or as an addendum to the NOTICE text from the Work, provided * that such additional attribution notices cannot be construed * as modifying the License. * * You may add Your own copyright statement to Your modifications and * may provide additional or different license terms and conditions * for use, reproduction, or distribution of Your modifications, or * for any such Derivative Works as a whole, provided Your use, * reproduction, and distribution of the Work otherwise complies with * the conditions stated in this License. * * 5. Submission of Contributions. Unless You explicitly state otherwise, * any Contribution intentionally submitted for inclusion in the Work * by You to the Licensor shall be under the terms and conditions of * this License, without any additional terms or conditions. * Notwithstanding the above, nothing herein shall supersede or modify * the terms of any separate license agreement you may have executed * with Licensor regarding such Contributions. * * 6. Trademarks. This License does not grant permission to use the trade * names, trademarks, service marks, or product names of the Licensor, * except as required for reasonable and customary use in describing the * origin of the Work and reproducing the content of the NOTICE file. * * 7. Disclaimer of Warranty. Unless required by applicable law or * agreed to in writing, Licensor provides the Work (and each * Contributor provides its Contributions) on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied, including, without limitation, any warranties or conditions * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A * PARTICULAR PURPOSE. You are solely responsible for determining the * appropriateness of using or redistributing the Work and assume any * risks associated with Your exercise of permissions under this License. * * 8. Limitation of Liability. In no event and under no legal theory, * whether in tort (including negligence), contract, or otherwise, * unless required by applicable law (such as deliberate and grossly * negligent acts) or agreed to in writing, shall any Contributor be * liable to You for damages, including any direct, indirect, special, * incidental, or consequential damages of any character arising as a * result of this License or out of the use or inability to use the * Work (including but not limited to damages for loss of goodwill, * work stoppage, computer failure or malfunction, or any and all * other commercial damages or losses), even if such Contributor * has been advised of the possibility of such damages. * * 9. Accepting Warranty or Additional Liability. While redistributing * the Work or Derivative Works thereof, You may choose to offer, * and charge a fee for, acceptance of support, warranty, indemnity, * or other liability obligations and/or rights consistent with this * License. However, in accepting such obligations, You may act only * on Your own behalf and on Your sole responsibility, not on behalf * of any other Contributor, and only if You agree to indemnify, * defend, and hold each Contributor harmless for any liability * incurred by, or claims asserted against, such Contributor by reason * of your accepting any such warranty or additional liability. * * END OF TERMS AND CONDITIONS * * APPENDIX: How to apply the Apache License to your work. * * To apply the Apache License to your work, attach the following * boilerplate notice, with the fields enclosed by brackets "[]" * replaced with your own identifying information. (Don't include * the brackets!) The text should be enclosed in the appropriate * comment syntax for the file format. We also recommend that a * file or class name and description of purpose be included on the * same "printed page" as the copyright notice for easier * identification within third-party archives. * * Copyright 2016 Alibaba Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * */ package com.taobao.common.dexpatcher; import com.taobao.common.dexpatcher.algorithms.diff.utils.FileOperation; import com.taobao.common.dexpatcher.algorithms.diff.utils.TinkerPatchException; import com.taobao.common.dexpatcher.algorithms.diff.utils.TypedValue; import com.taobao.common.dexpatcher.algorithms.diff.utils.Utils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.regex.Pattern; public class Configuration { protected static final String TAG_ISSUE = "issue"; protected static final String DEX_ISSUE = "dex"; protected static final String SO_ISSUE = "lib"; protected static final String RES_ISSUE = "resource"; protected static final String SIGN_ISSUE = "sign"; protected static final String PACKAGE_CONFIG_ISSUE = "packageConfig"; protected static final String PROPERTY_ISSUE = "property"; protected static final String ATTR_ID = "id"; protected static final String ATTR_VALUE = "value"; protected static final String ATTR_NAME = "name"; protected static final String ATTR_IGNORE_WARNING = "ignoreWarning"; protected static final String ATTR_USE_SIGN = "useSign"; protected static final String ATTR_SEVEN_ZIP_PATH = "sevenZipPath"; protected static final String ATTR_DEX_MODE = "dexMode"; protected static final String ATTR_PATTERN = "pattern"; protected static final String ATTR_RES_IGNORE_CHANGE = "ignoreChange"; protected static final String ATTR_RES_LARGE_MOD = "largeModSize"; protected static final String ATTR_LOADER = "loader"; protected static final String ATTR_CONFIG_FIELD = "configField"; protected static final String ATTR_SIGN_FILE_PATH = "path"; protected static final String ATTR_SIGN_FILE_KEYPASS = "keypass"; protected static final String ATTR_SIGN_FILE_STOREPASS = "storepass"; protected static final String ATTR_SIGN_FILE_ALIAS = "alias"; /** * base config data */ public String mOldApkPath; public String mNewApkPath; public String mOutFolder; public File mOldApkFile; public File mNewApkFile; public boolean mIgnoreWarning; public boolean mUsePreGeneratedPatchDex; /** * lib config */ public HashSet<Pattern> mSoFilePattern; /** * dex config */ public HashSet<Pattern> mDexFilePattern; public HashSet<String> mDexLoaderPattern; public boolean mDexRaw; /** * resource config */ public HashSet<Pattern> mResFilePattern; public HashSet<Pattern> mResIgnoreChangePattern; public HashSet<String> mResRawPattern; public int mLargeModSize; /** * only gradle have the param */ public boolean mUseApplyResource; /** * package file config */ public HashMap<String, String> mPackageFields; /** * sevenZip path config */ public String mSevenZipPath; /** * sign data */ public boolean mUseSignAPk; public File mSignatureFile; public String mKeyPass; public String mStoreAlias; public String mStorePass; /** * temp files */ public File mTempResultDir; public File mTempUnzipOldDir; public File mTempUnzipNewDir; public boolean mUsingGradle; /** * use by command line with xml config */ public Configuration(File config, File outputFile, File oldApkFile, File newApkFile) throws IOException, ParserConfigurationException, SAXException, TinkerPatchException { mUsingGradle = false; mSoFilePattern = new HashSet<Pattern>(); mDexFilePattern = new HashSet<Pattern>(); mDexLoaderPattern = new HashSet<String>(); mResFilePattern = new HashSet<Pattern>(); mResRawPattern = new HashSet<String>(); mResIgnoreChangePattern = new HashSet<Pattern>(); mPackageFields = new HashMap<String, String>(); mOutFolder = outputFile.getAbsolutePath(); FileOperation.cleanDir(outputFile); mOldApkFile = oldApkFile; mOldApkPath = oldApkFile.getAbsolutePath(); mNewApkFile = newApkFile; mNewApkPath = newApkFile.getAbsolutePath(); mLargeModSize = 100; readXmlConfig(config); createTempDirectory(); checkInputPatternParameter(); } /** * use by gradle */ public Configuration(InputParam param) throws IOException, TinkerPatchException { mUsingGradle = true; mSoFilePattern = new HashSet<Pattern>(); mDexFilePattern = new HashSet<Pattern>(); mDexLoaderPattern = new HashSet<String>(); mResFilePattern = new HashSet<Pattern>(); mResRawPattern = new HashSet<String>(); mResIgnoreChangePattern = new HashSet<Pattern>(); mPackageFields = new HashMap<String, String>(); for (String item : param.soFilePattern) { addToPatterns(item, mSoFilePattern); } for (String item : param.dexFilePattern) { addToPatterns(item, mDexFilePattern); } for (String item : param.resourceFilePattern) { mResRawPattern.add(item); addToPatterns(item, mResFilePattern); } for (String item : param.resourceIgnoreChangePattern) { addToPatterns(item, mResIgnoreChangePattern); } mLargeModSize = param.largeModSize; //only gradle have the param mUseApplyResource = param.useApplyResource; mDexLoaderPattern.addAll(param.dexLoaderPattern); //can be only raw or jar if (param.dexMode.equals("raw")) { mDexRaw = true; } mOldApkPath = param.oldApk; mOldApkFile = new File(mOldApkPath); mNewApkPath = param.newApk; mNewApkFile = new File(mNewApkPath); mOutFolder = param.outFolder; mIgnoreWarning = param.ignoreWarning; mUsePreGeneratedPatchDex = param.usePreGeneratedPatchDex; mSevenZipPath = param.sevenZipPath; mPackageFields = param.configFields; mUseSignAPk = param.useSign; setSignData(param.signFile, param.keypass, param.storealias, param.storepass); FileOperation.cleanDir(new File(mOutFolder)); createTempDirectory(); checkInputPatternParameter(); } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("configuration: \n"); sb.append("oldApk:" + mOldApkPath + "\n"); sb.append("newApk:" + mNewApkPath + "\n"); sb.append("outputFolder:" + mOutFolder + "\n"); sb.append("isIgnoreWarning:" + mIgnoreWarning + "\n"); sb.append("isInsertStubMode:" + mUsePreGeneratedPatchDex + "\n"); sb.append("7-ZipPath:" + mSevenZipPath + "\n"); sb.append("useSignAPk:" + mUseSignAPk + "\n"); sb.append("package meta fields: \n"); for (String name : mPackageFields.keySet()) { sb.append("filed name:" + name + ", filed value:" + mPackageFields.get(name) + "\n"); } sb.append("dex configs: \n"); if (mDexRaw) { sb.append("dexMode: raw" + "\n"); } else { sb.append("dexMode: jar" + "\n"); } for (Pattern name : mDexFilePattern) { sb.append("dexPattern:" + name.toString() + "\n"); } for (String name : mDexLoaderPattern) { sb.append("dex loader:" + name + "\n"); } sb.append("lib configs: \n"); for (Pattern name : mSoFilePattern) { sb.append("libPattern:" + name.toString() + "\n"); } sb.append("resource configs: \n"); for (Pattern name : mResFilePattern) { sb.append("resPattern:" + name.toString() + "\n"); } for (Pattern name : mResIgnoreChangePattern) { sb.append("resIgnore change:" + name.toString() + "\n"); } sb.append("largeModSize:" + mLargeModSize + "kb\n"); sb.append("useApplyResource:" + mUseApplyResource + "\n"); return sb.toString(); } private void createTempDirectory() throws TinkerPatchException { mTempResultDir = new File(mOutFolder + "/" + TypedValue.PATH_PATCH_FILES); FileOperation.deleteDir(mTempResultDir); if (!mTempResultDir.exists()) { mTempResultDir.mkdir(); } String oldApkName = mOldApkFile.getName(); if (!oldApkName.endsWith(TypedValue.FILE_APK)) { throw new TinkerPatchException( String.format("input apk file path must end with .apk, yours %s\n", oldApkName) ); } String newApkName = mNewApkFile.getName(); if (!newApkName.endsWith(TypedValue.FILE_APK)) { throw new TinkerPatchException( String.format("input apk file path must end with .apk, yours %s\n", newApkName) ); } String tempOldName = oldApkName.substring(0, oldApkName.indexOf(TypedValue.FILE_APK)); String tempNewName = newApkName.substring(0, newApkName.indexOf(TypedValue.FILE_APK)); if (tempNewName.equals(tempOldName)) { tempOldName += "-old"; tempNewName += "-new"; } mTempUnzipOldDir = new File(mOutFolder, tempOldName); mTempUnzipNewDir = new File(mOutFolder, tempNewName); } public void setSignData(File signatureFile, String keypass, String storealias, String storepass) throws IOException { if (mUseSignAPk) { mSignatureFile = signatureFile; if (!mSignatureFile.exists()) { throw new IOException( String.format("the signature file do not exit, raw path= %s\n", mSignatureFile.getAbsolutePath()) ); } mKeyPass = keypass; mStoreAlias = storealias; mStorePass = storepass; } } private void checkInputPatternParameter() throws TinkerPatchException { if (mSoFilePattern.isEmpty() && mDexFilePattern.isEmpty() && mResFilePattern.isEmpty()) { throw new TinkerPatchException("no dex, so or resource pattern are found"); } if (mLargeModSize <= 0) { throw new TinkerPatchException("largeModSize must be larger than 0"); } } /** * read args from xml **/ void readXmlConfig(File xmlConfigFile) throws IOException, ParserConfigurationException, SAXException { if (!xmlConfigFile.exists()) { return; } System.out.printf("reading config file, %s\n", xmlConfigFile.getAbsolutePath()); BufferedInputStream input = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); input = new BufferedInputStream(new FileInputStream(xmlConfigFile)); InputSource source = new InputSource(input); factory.setNamespaceAware(false); factory.setValidating(false); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(source); NodeList issues = document.getElementsByTagName(TAG_ISSUE); for (int i = 0, count = issues.getLength(); i < count; i++) { Node node = issues.item(i); Element element = (Element) node; String id = element.getAttribute(ATTR_ID); if (id.length() == 0) { System.err.println("Invalid config file: Missing required issue id attribute"); continue; } if (id.equals(PROPERTY_ISSUE)) { readPropertyFromXml(node); } else if (id.equals(DEX_ISSUE)) { readDexPatternsFromXml(node); } else if (id.equals(SO_ISSUE)) { readLibPatternsFromXml(node); } else if (id.equals(RES_ISSUE)) { readResPatternsFromXml(node); } else if (id.equals(PACKAGE_CONFIG_ISSUE)) { readPackageConfigFromXml(node); } else if (id.equals(SIGN_ISSUE)) { if (mUseSignAPk) { readSignFromXml(node); } } else { System.err.println("unknown issue " + id); } } } finally { if (input != null) { try { input.close(); } catch (IOException e) { System.exit(-1); } } } } private void readPropertyFromXml(Node node) throws IOException { NodeList childNodes = node.getChildNodes(); if (childNodes.getLength() > 0) { for (int j = 0, n = childNodes.getLength(); j < n; j++) { Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE) { Element check = (Element) child; String tagName = check.getTagName(); String value = check.getAttribute(ATTR_VALUE); if (value.length() == 0) { throw new IOException( String.format("Invalid config file: Missing required attribute %s\n", ATTR_VALUE) ); } if (tagName.equals(ATTR_IGNORE_WARNING)) { mIgnoreWarning = value.equals("true"); } else if (tagName.equals(ATTR_USE_SIGN)) { mUseSignAPk = value.equals("true"); } else if (tagName.equals(ATTR_SEVEN_ZIP_PATH)) { File sevenZipFile = new File(value); if (sevenZipFile.exists()) { mSevenZipPath = value; } else { mSevenZipPath = "7za"; } } else { System.err.println("unknown property tag " + tagName); } } } } } private void readSignFromXml(Node node) throws IOException { if (mSignatureFile != null) { System.err.println("already set the sign info from command line, ignore this"); return; } NodeList childNodes = node.getChildNodes(); if (childNodes.getLength() > 0) { for (int j = 0, n = childNodes.getLength(); j < n; j++) { Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE) { Element check = (Element) child; String tagName = check.getTagName(); String value = check.getAttribute(ATTR_VALUE); if (value.length() == 0) { throw new IOException( String.format("Invalid config file: Missing required attribute %s\n", ATTR_VALUE) ); } if (tagName.equals(ATTR_SIGN_FILE_PATH)) { mSignatureFile = new File(value); if (!mSignatureFile.exists()) { throw new IOException( String.format("the signature file do not exit, raw path= %s\n", mSignatureFile.getAbsolutePath()) ); } } else if (tagName.equals(ATTR_SIGN_FILE_STOREPASS)) { mStorePass = value; mStorePass = mStorePass.trim(); } else if (tagName.equals(ATTR_SIGN_FILE_KEYPASS)) { mKeyPass = value; mKeyPass = mKeyPass.trim(); } else if (tagName.equals(ATTR_SIGN_FILE_ALIAS)) { mStoreAlias = value; mStoreAlias = mStoreAlias.trim(); } else { System.err.println("unknown sign tag " + tagName); } } } } } private void readDexPatternsFromXml(Node node) throws IOException { NodeList childNodes = node.getChildNodes(); if (childNodes.getLength() > 0) { for (int j = 0, n = childNodes.getLength(); j < n; j++) { Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE) { Element check = (Element) child; String tagName = check.getTagName(); String value = check.getAttribute(ATTR_VALUE); if (tagName.equals(ATTR_DEX_MODE)) { if (value.equals("raw")) { mDexRaw = true; } } else if (tagName.equals(ATTR_PATTERN)) { addToPatterns(value, mDexFilePattern); } else if (tagName.equals(ATTR_LOADER)) { mDexLoaderPattern.add(value); } else { System.err.println("unknown dex tag " + tagName); } } } } } private void readLibPatternsFromXml(Node node) throws IOException { NodeList childNodes = node.getChildNodes(); if (childNodes.getLength() > 0) { for (int j = 0, n = childNodes.getLength(); j < n; j++) { Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE) { Element check = (Element) child; String tagName = check.getTagName(); String value = check.getAttribute(ATTR_VALUE); if (tagName.equals(ATTR_PATTERN)) { addToPatterns(value, mSoFilePattern); } else { System.err.println("unknown dex tag " + tagName); } } } } } private void readResPatternsFromXml(Node node) throws IOException { NodeList childNodes = node.getChildNodes(); if (childNodes.getLength() > 0) { for (int j = 0, n = childNodes.getLength(); j < n; j++) { Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE) { Element check = (Element) child; String tagName = check.getTagName(); String value = check.getAttribute(ATTR_VALUE); if (tagName.equals(ATTR_PATTERN)) { mResRawPattern.add(value); addToPatterns(value, mResFilePattern); } else if (tagName.equals(ATTR_RES_IGNORE_CHANGE)) { addToPatterns(value, mResIgnoreChangePattern); } else if (tagName.equals(ATTR_RES_LARGE_MOD)) { mLargeModSize = Integer.valueOf(value); } else { System.err.println("unknown dex tag " + tagName); } } } } } private void readPackageConfigFromXml(Node node) throws IOException { NodeList childNodes = node.getChildNodes(); if (childNodes.getLength() > 0) { for (int j = 0, n = childNodes.getLength(); j < n; j++) { Node child = childNodes.item(j); if (child.getNodeType() == Node.ELEMENT_NODE) { Element check = (Element) child; String tagName = check.getTagName(); String value = check.getAttribute(ATTR_VALUE); String name = check.getAttribute(ATTR_NAME); if (tagName.equals(ATTR_CONFIG_FIELD)) { mPackageFields.put(name, value); } else { System.err.println("unknown package config tag " + tagName); } } } } } private void addToPatterns(String value, HashSet<Pattern> patterns) throws IOException { if (value.length() == 0) { throw new IOException( String.format("Invalid config file: Missing required attribute %s\n", ATTR_VALUE) ); } value = Utils.convertToPatternString(value); Pattern pattern = Pattern.compile(value); patterns.add(pattern); } }