View Javadoc

1   /****************************************************************************
2    * Copyright (c) 2005, 2006, 2007, 2008, 2009 Imola Informatica.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the LGPL License v2.1
5    * which accompanies this distribution, and is available at
6    * http://www.gnu.org/licenses/lgpl.html
7    ****************************************************************************/
8   package it.imolinfo.jbi4corba.jbi.cxf;
9   
10  import java.io.IOException;
11  import java.io.LineNumberReader;
12  import java.net.URL;
13  import java.util.HashSet;
14  import java.util.Set;
15  import java.util.Stack;
16  
17  import org.apache.cxf.tools.corba.idlpreprocessor.DefineState;
18  import org.apache.cxf.tools.corba.idlpreprocessor.IncludeResolver;
19  import org.apache.cxf.tools.corba.idlpreprocessor.PreprocessingException;
20  
21  /**
22   * A Reader that implements the #include functionality of the preprocessor.
23   * Starting from one URL, it generates one stream of characters by tracking
24   * #defines, #ifdefs, etc. and following #includes accordingly.
25   * 
26   * <p>
27   * This reader augments the stream with <a
28   * href="http://gcc.gnu.org/onlinedocs/gcc-3.2.3/cpp/Preprocessor-Output.html">
29   * location information</a> when the source URL is switched. This improves error
30   * reporting (with correct file and linenumber information) in the subsequent
31   * compilation steps like IDL parsing and also allows the implentation of code
32   * generation options like the -emitAll flag available in the JDK idlj tool.
33   * </p>
34   */
35  public final class Jbi4CorbaIdlPreprocessor /*extends Reader*/ {
36  
37  	/**
38  	 * Maximum depth of {@link #includeStack} to prevent infinite recursion.
39  	 */
40  	private static final int MAX_INCLUDE_DEPTH = 64;
41  
42  	/**
43  	 * GNU standard preprocessor output flag for signalling a new file.
44  	 * 
45  	 * @see http://gcc.gnu.org/onlinedocs/gcc-3.2.3/cpp/Preprocessor-Output.html
46  	 */
47  	private static final char PUSH = '1';
48  
49  	/**
50  	 * GNU standard preprocessor output flag for signalling returning to a file.
51  	 * 
52  	 * @see http://gcc.gnu.org/onlinedocs/gcc-3.2.3/cpp/Preprocessor-Output.html
53  	 */
54  	private static final char POP = '2';
55  
56  	private static final String LF = System.getProperty("line.separator");
57  
58  	private final IncludeResolver includeResolver;
59  
60  	private final Stack<Jbi4CorbaIncludeStackEntry> includeStack = new Stack<Jbi4CorbaIncludeStackEntry>();
61          
62          private final Set<String> includeSet=new HashSet<String>();
63          
64          private final Set<URL> idlSet=new HashSet<URL>();
65  
66  	/**
67  	 * Stack of Booleans, corresponding to nested 'if' preprocessor directives.
68  	 * The top of the stack signals whether the current idl code is skipped.
69  	 * 
70  	 * @see #skips()
71  	 */
72  	private final Stack<Boolean> ifStack = new Stack<Boolean>();
73  
74  	private final DefineState defineState;
75  
76  	private int readPos;
77  
78  	private static final String ORBFNAME = "orb.idl";
79  	private static final String IRFNAME = "ir.idl";
80  
81  	/**
82  	 * Creates a new IncludeReader.
83  	 * 
84  	 * @param startURL
85  	 * @param startLocation
86  	 * @param includeResolver
87  	 * @param defineState
88  	 * @throws IOException
89  	 */
90  	public Jbi4CorbaIdlPreprocessor(URL startURL, String startLocation,
91  			IncludeResolver resolver, DefineState state) throws IOException {
92  		this.includeResolver = resolver;
93  		this.defineState = state;
94  		pushInclude(startURL, startLocation);
95  		fillBuffer();
96  	}
97  
98      
99          
100 
101 	/**
102 	 * @param url
103 	 * @throws IOException
104 	 */
105 	private void pushInclude(URL url, String location) throws IOException {
106 		final Jbi4CorbaIncludeStackEntry includeStackEntry = new Jbi4CorbaIncludeStackEntry(
107 				url, location);
108 		includeStack.push(includeStackEntry);
109 		
110 	}
111 
112 
113 
114 	private void fillBuffer() throws IOException {
115 		while (!includeStack.isEmpty()) {
116 			LineNumberReader reader = getReader();
117 			final int lineNo = reader.getLineNumber();
118 			String line = reader.readLine();
119 
120 			if (line == null) {
121 				popInclude();
122 				continue;
123 			}
124 			
125 			if (!line.trim().startsWith("#")) {				
126 				continue;
127 			}
128 
129 			final Jbi4CorbaIncludeStackEntry ise = includeStack.peek();
130 			line = line.trim();
131 
132 			if (line.startsWith("#include") && (line.contains(ORBFNAME) || line.contains(IRFNAME))) {
133                             
134                             if(!idlSet.contains(getCurrentURL())){
135                                 idlSet.add(getCurrentURL());
136                             }
137                             
138 			} 
139                         
140                         else if (line.startsWith("#include")) {
141 				handleInclude(line, lineNo, ise);
142                         }
143 			
144 		}
145 	}
146 	
147 
148 	
149 
150 	private void handleInclude(String line, int lineNo,
151 			final Jbi4CorbaIncludeStackEntry ise) throws IOException {
152 
153 		if (skips()) {
154 			
155 			return;
156 		}
157 
158 		if (includeStack.size() >= MAX_INCLUDE_DEPTH) {
159 			throw new PreprocessingException(
160 					"more than "
161 							+ MAX_INCLUDE_DEPTH
162 							+ " nested #includes - assuming infinite recursion, aborting",
163 					ise.getURL(), lineNo);
164 		}
165 
166 		String arg = line.replaceFirst("#include", "").trim();		
167 		if (arg.length() == 0) {
168 			throw new PreprocessingException("#include without an argument",
169 					ise.getURL(), lineNo);
170 		}
171 
172 		char first = arg.charAt(0);
173 		final int lastIdx = arg.length() - 1;
174 		char last = arg.charAt(lastIdx);
175 		if (arg.length() < 3 || !(first == '<' && last == '>')
176 				&& !(first == '"' && last == '"')) {
177 			throw new PreprocessingException(
178 					"argument for '#include' must be enclosed in '< >' or '\" \"'",
179 					ise.getURL(), lineNo);
180 		}
181 		String spec = arg.substring(1, lastIdx);
182 		//File URL
183 		URL include = (first == '<') ? includeResolver.findSystemInclude(spec)
184 				: includeResolver.findUserInclude(spec);
185 
186 		if (include == null) {
187 			throw new PreprocessingException("unable to resolve include '"
188 					+ spec + "'", ise.getURL(), lineNo);
189 		}
190                 
191 		pushInclude(include, spec);
192                 
193                 if(!includeSet.contains(spec)){
194                      includeSet.add(spec);
195                 }
196                
197 	}
198 
199 	private void popInclude() throws IOException {
200 		final Jbi4CorbaIncludeStackEntry poppedStackEntry = includeStack.pop();
201 		try {
202 			if (includeStack.size() > 0) {
203 				final Jbi4CorbaIncludeStackEntry newTopEntry = includeStack
204 						.peek();
205 				final LineNumberReader reader = getReader();
206 //				final int lineNumber = reader.getLineNumber();
207 //				final String location = newTopEntry.getLocation();                        				
208 			}
209 		} finally {
210 			poppedStackEntry.getReader().close();
211 		}
212 	}
213 
214 	private boolean skips() {
215 		if (ifStack.isEmpty()) {
216 			return false;
217 		}
218 
219 		return ifStack.peek();
220 	}
221 
222 
223 	private LineNumberReader getReader() {
224 		Jbi4CorbaIncludeStackEntry topOfStack = includeStack.peek();
225 		return topOfStack.getReader();
226 	}
227         
228         private URL getCurrentURL(){
229                 Jbi4CorbaIncludeStackEntry topOfStack = includeStack.peek();
230                 return topOfStack.getURL();
231         }
232 
233 
234         
235         /**
236          * Return the set that contains all the information for the inclusion of idl in the wsdl
237          */
238         public Set<String> getIncludesPath() {
239             return includeSet;
240         }
241         
242         /**
243          * Return all idl Url that contain #include orb.idl ir.idl
244          */
245         public Set<URL> getIdlURL(){
246             return idlSet;
247         }
248         
249 }