1 /******************************************************************************* 2 * Copyright (c) 2000, 2013 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.compiler.util; 15 16 import java.io.IOException; 17 import java.io.InputStream; 18 import java.util.ArrayList; 19 import java.util.List; 20 21 @SuppressWarnings({"rawtypes", "unchecked"}) 22 public class ManifestAnalyzer { 23 private static final int 24 START = 0, 25 IN_CLASSPATH_HEADER = 1, // multistate 26 PAST_CLASSPATH_HEADER = 2, 27 SKIPPING_WHITESPACE = 3, 28 READING_JAR = 4, 29 CONTINUING = 5, 30 SKIP_LINE = 6; 31 private static final char[] CLASSPATH_HEADER_TOKEN = 32 "Class-Path:".toCharArray(); //$NON-NLS-1$ 33 private int classpathSectionsCount; 34 private ArrayList calledFilesNames; 35 36 /** 37 * Analyzes the manifest contents. The given input stream is read using a UTF-8 encoded reader. 38 * If the contents of the input stream is not encoded using a UTF-8 encoding, the analysis will fail. 39 * 40 * @param inputStream the given input stream. 41 * 42 * @return <code>true</code> if the analysis is successful, <code>false</code> otherwise. 43 * @throws IOException if an exception occurs while analyzing the file 44 */ analyzeManifestContents(InputStream inputStream)45 public boolean analyzeManifestContents(InputStream inputStream) throws IOException { 46 char[] chars = Util.getInputStreamAsCharArray(inputStream, -1, Util.UTF_8); 47 return analyzeManifestContents(chars); 48 } 49 50 /** 51 * Analyzes the manifest contents. 52 * 53 * @param chars the content of the manifest 54 * 55 * @return <code>true</code> if the analysis is successful, <code>false</code> otherwise. 56 */ analyzeManifestContents(char[] chars)57 public boolean analyzeManifestContents(char[] chars) { 58 int state = START, substate = 0; 59 StringBuffer currentJarToken = new StringBuffer(); 60 int currentChar; 61 this.classpathSectionsCount = 0; 62 this.calledFilesNames = null; 63 for (int i = 0, max = chars.length; i < max;) { 64 currentChar = chars[i++]; 65 if (currentChar == '\r') { 66 // skip \r, will consider \n later (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=251079 ) 67 if (i < max) { 68 currentChar = chars[i++]; 69 } 70 } 71 switch (state) { 72 case START: 73 if (currentChar == CLASSPATH_HEADER_TOKEN[0]) { 74 state = IN_CLASSPATH_HEADER; 75 substate = 1; 76 } else { 77 state = SKIP_LINE; 78 } 79 break; 80 case IN_CLASSPATH_HEADER: 81 if (currentChar == '\n') { 82 state = START; 83 } else if (currentChar != CLASSPATH_HEADER_TOKEN[substate++]) { 84 state = SKIP_LINE; 85 } else if (substate == CLASSPATH_HEADER_TOKEN.length) { 86 state = PAST_CLASSPATH_HEADER; 87 } 88 break; 89 case PAST_CLASSPATH_HEADER: 90 if (currentChar == ' ') { 91 state = SKIPPING_WHITESPACE; 92 this.classpathSectionsCount++; 93 } else { 94 return false; 95 } 96 break; 97 case SKIPPING_WHITESPACE: 98 if (currentChar == '\n') { 99 state = CONTINUING; 100 } else if (currentChar != ' ') { 101 currentJarToken.append((char) currentChar); 102 state = READING_JAR; 103 } else { 104 // >>>>>>>>>>>>>>>>>> Add the latest jar read 105 addCurrentTokenJarWhenNecessary(currentJarToken); 106 } 107 break; 108 case CONTINUING: 109 if (currentChar == '\n') { 110 addCurrentTokenJarWhenNecessary(currentJarToken); 111 state = START; 112 } else if (currentChar == ' ') { 113 state = SKIPPING_WHITESPACE; 114 } else if (currentChar == CLASSPATH_HEADER_TOKEN[0]) { 115 addCurrentTokenJarWhenNecessary(currentJarToken); 116 state = IN_CLASSPATH_HEADER; 117 substate = 1; 118 } else if (this.calledFilesNames == null) { 119 // >>>>>>>>>>>>>>>>>> Add the latest jar read 120 addCurrentTokenJarWhenNecessary(currentJarToken); 121 state = START; 122 } else { 123 // >>>>>>>>>>>>>>>>>> Add the latest jar read 124 addCurrentTokenJarWhenNecessary(currentJarToken); 125 state = SKIP_LINE; 126 } 127 break; 128 case SKIP_LINE: 129 if (currentChar == '\n') { 130 state = START; 131 } 132 break; 133 case READING_JAR: 134 if (currentChar == '\n') { 135 // appends token below 136 state = CONTINUING; 137 // >>>>>>>>>>> Add a break to not add the jar yet as it can continue on the next line 138 break; 139 } else if (currentChar == ' ') { 140 // appends token below 141 state = SKIPPING_WHITESPACE; 142 } else { 143 currentJarToken.append((char) currentChar); 144 break; 145 } 146 addCurrentTokenJarWhenNecessary(currentJarToken); 147 break; 148 } 149 } 150 switch (state) { 151 case START: 152 return true; 153 case IN_CLASSPATH_HEADER: 154 return true; 155 case PAST_CLASSPATH_HEADER: 156 return false; 157 case SKIPPING_WHITESPACE: 158 // >>>>>>>>>>>>>>>>>> Add the latest jar read 159 addCurrentTokenJarWhenNecessary(currentJarToken); 160 return true; 161 case CONTINUING: 162 // >>>>>>>>>>>>>>>>>> Add the latest jar read 163 addCurrentTokenJarWhenNecessary(currentJarToken); 164 return true; 165 case SKIP_LINE: 166 if (this.classpathSectionsCount != 0) { 167 if (this.calledFilesNames == null) { 168 return false; 169 } 170 } 171 return true; 172 case READING_JAR: 173 // >>>>>>>>>>>>>>>>>> Add the latest jar read 174 return false; 175 } 176 return true; 177 } 178 179 // >>>>>>>>>>>>>>>> Method Extracted from analyzeManifestContents in the READING_JAR Block addCurrentTokenJarWhenNecessary(StringBuffer currentJarToken)180 private boolean addCurrentTokenJarWhenNecessary(StringBuffer currentJarToken) { 181 if (currentJarToken != null && currentJarToken.length() > 0) { 182 if (this.calledFilesNames == null) { 183 this.calledFilesNames = new ArrayList(); 184 } 185 this.calledFilesNames.add(currentJarToken.toString()); 186 currentJarToken.setLength(0); 187 return true; 188 } 189 return false; 190 } 191 // <<<<<<<<<<<<<<<<<<<<<< 192 193 getClasspathSectionsCount()194 public int getClasspathSectionsCount() { 195 return this.classpathSectionsCount; 196 } getCalledFileNames()197 public List getCalledFileNames() { 198 return this.calledFilesNames; 199 } 200 } 201