OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2013, the Dart project authors. | 2 * Copyright (c) 2013, the Dart project authors. |
3 * | 3 * |
4 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not u
se this file except | 4 * Licensed under the Eclipse Public License v1.0 (the "License"); you may not u
se this file except |
5 * in compliance with the License. You may obtain a copy of the License at | 5 * in compliance with the License. You may obtain a copy of the License at |
6 * | 6 * |
7 * http://www.eclipse.org/legal/epl-v10.html | 7 * http://www.eclipse.org/legal/epl-v10.html |
8 * | 8 * |
9 * Unless required by applicable law or agreed to in writing, software distribut
ed under the License | 9 * Unless required by applicable law or agreed to in writing, software distribut
ed under the License |
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY K
IND, either express | 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY K
IND, either express |
11 * or implied. See the License for the specific language governing permissions a
nd limitations under | 11 * or implied. See the License for the specific language governing permissions a
nd limitations under |
12 * the License. | 12 * the License. |
13 */ | 13 */ |
14 package com.google.dart.engine.internal.builder; | 14 package com.google.dart.engine.internal.builder; |
15 | 15 |
16 import com.google.common.annotations.VisibleForTesting; | 16 import com.google.common.annotations.VisibleForTesting; |
17 import com.google.dart.engine.AnalysisEngine; | 17 import com.google.dart.engine.AnalysisEngine; |
18 import com.google.dart.engine.ast.CompilationUnit; | 18 import com.google.dart.engine.ast.Expression; |
19 import com.google.dart.engine.context.AnalysisException; | 19 import com.google.dart.engine.context.AnalysisException; |
20 import com.google.dart.engine.element.HtmlScriptElement; | 20 import com.google.dart.engine.element.HtmlScriptElement; |
21 import com.google.dart.engine.error.AnalysisError; | 21 import com.google.dart.engine.error.AnalysisError; |
22 import com.google.dart.engine.error.ErrorCode; | 22 import com.google.dart.engine.error.ErrorCode; |
23 import com.google.dart.engine.error.HtmlWarningCode; | 23 import com.google.dart.engine.error.HtmlWarningCode; |
| 24 import com.google.dart.engine.html.ast.EmbeddedExpression; |
| 25 import com.google.dart.engine.html.ast.HtmlScriptTagNode; |
24 import com.google.dart.engine.html.ast.HtmlUnit; | 26 import com.google.dart.engine.html.ast.HtmlUnit; |
25 import com.google.dart.engine.html.ast.XmlAttributeNode; | 27 import com.google.dart.engine.html.ast.XmlAttributeNode; |
26 import com.google.dart.engine.html.ast.XmlTagNode; | 28 import com.google.dart.engine.html.ast.XmlTagNode; |
27 import com.google.dart.engine.html.ast.visitor.XmlVisitor; | 29 import com.google.dart.engine.html.ast.visitor.XmlVisitor; |
28 import com.google.dart.engine.html.scanner.Token; | |
29 import com.google.dart.engine.html.scanner.TokenType; | 30 import com.google.dart.engine.html.scanner.TokenType; |
30 import com.google.dart.engine.internal.context.InternalAnalysisContext; | 31 import com.google.dart.engine.internal.context.InternalAnalysisContext; |
31 import com.google.dart.engine.internal.context.RecordingErrorListener; | 32 import com.google.dart.engine.internal.context.RecordingErrorListener; |
32 import com.google.dart.engine.internal.element.EmbeddedHtmlScriptElementImpl; | 33 import com.google.dart.engine.internal.element.EmbeddedHtmlScriptElementImpl; |
33 import com.google.dart.engine.internal.element.ExternalHtmlScriptElementImpl; | 34 import com.google.dart.engine.internal.element.ExternalHtmlScriptElementImpl; |
34 import com.google.dart.engine.internal.element.HtmlElementImpl; | 35 import com.google.dart.engine.internal.element.HtmlElementImpl; |
35 import com.google.dart.engine.internal.element.LibraryElementImpl; | 36 import com.google.dart.engine.internal.element.LibraryElementImpl; |
36 import com.google.dart.engine.internal.resolver.Library; | 37 import com.google.dart.engine.internal.resolver.Library; |
37 import com.google.dart.engine.internal.resolver.LibraryResolver; | 38 import com.google.dart.engine.internal.resolver.LibraryResolver; |
38 import com.google.dart.engine.parser.Parser; | |
39 import com.google.dart.engine.scanner.Scanner; | |
40 import com.google.dart.engine.scanner.SubSequenceReader; | |
41 import com.google.dart.engine.source.Source; | 39 import com.google.dart.engine.source.Source; |
42 import com.google.dart.engine.utilities.io.UriUtilities; | 40 import com.google.dart.engine.utilities.io.UriUtilities; |
43 import com.google.dart.engine.utilities.source.LineInfo; | 41 import com.google.dart.engine.utilities.source.LineInfo; |
44 import com.google.dart.engine.utilities.source.LineInfo.Location; | |
45 | 42 |
46 import java.net.URI; | 43 import java.net.URI; |
47 import java.net.URISyntaxException; | 44 import java.net.URISyntaxException; |
48 import java.util.ArrayList; | 45 import java.util.ArrayList; |
49 import java.util.HashSet; | 46 import java.util.HashSet; |
50 import java.util.Set; | 47 import java.util.Set; |
51 | 48 |
52 /** | 49 /** |
53 * Instances of the class {@code HtmlUnitBuilder} build an element model for a s
ingle HTML unit. | 50 * Instances of the class {@code HtmlUnitBuilder} build an element model for a s
ingle HTML unit. |
54 */ | 51 */ |
55 public class HtmlUnitBuilder implements XmlVisitor<Void> { | 52 public class HtmlUnitBuilder implements XmlVisitor<Void> { |
56 private static final String APPLICATION_DART_IN_DOUBLE_QUOTES = "\"application
/dart\""; | |
57 private static final String APPLICATION_DART_IN_SINGLE_QUOTES = "'application/
dart'"; | |
58 private static final String SCRIPT = "script"; | |
59 private static final String SRC = "src"; | 53 private static final String SRC = "src"; |
60 private static final String TYPE = "type"; | |
61 | 54 |
62 /** | 55 /** |
63 * The analysis context in which the element model will be built. | 56 * The analysis context in which the element model will be built. |
64 */ | 57 */ |
65 private final InternalAnalysisContext context; | 58 private final InternalAnalysisContext context; |
66 | 59 |
67 /** | 60 /** |
68 * The error listener to which errors will be reported. | 61 * The error listener to which errors will be reported. |
69 */ | 62 */ |
70 private RecordingErrorListener errorListener; | 63 private RecordingErrorListener errorListener; |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 /** | 149 /** |
157 * Return an array containing information about all of the libraries that were
resolved. | 150 * Return an array containing information about all of the libraries that were
resolved. |
158 * | 151 * |
159 * @return an array containing the libraries that were resolved | 152 * @return an array containing the libraries that were resolved |
160 */ | 153 */ |
161 public Set<Library> getResolvedLibraries() { | 154 public Set<Library> getResolvedLibraries() { |
162 return resolvedLibraries; | 155 return resolvedLibraries; |
163 } | 156 } |
164 | 157 |
165 @Override | 158 @Override |
| 159 public Void visitHtmlScriptTagNode(HtmlScriptTagNode node) { |
| 160 if (parentNodes.contains(node)) { |
| 161 return reportCircularity(node); |
| 162 } |
| 163 parentNodes.add(node); |
| 164 try { |
| 165 Source htmlSource = htmlElement.getSource(); |
| 166 XmlAttributeNode scriptAttribute = getScriptSourcePath(node); |
| 167 String scriptSourcePath = scriptAttribute == null ? null : scriptAttribute
.getText(); |
| 168 if (node.getAttributeEnd().getType() == TokenType.GT && scriptSourcePath =
= null) { |
| 169 EmbeddedHtmlScriptElementImpl script = new EmbeddedHtmlScriptElementImpl
(node); |
| 170 try { |
| 171 LibraryResolver resolver = new LibraryResolver(context); |
| 172 LibraryElementImpl library = (LibraryElementImpl) resolver.resolveEmbe
ddedLibrary( |
| 173 htmlSource, |
| 174 modificationStamp, |
| 175 node.getScript(), |
| 176 true); |
| 177 script.setScriptLibrary(library); |
| 178 resolvedLibraries.addAll(resolver.getResolvedLibraries()); |
| 179 errorListener.addAll(resolver.getErrorListener()); |
| 180 } catch (AnalysisException exception) { |
| 181 //TODO (danrubel): Handle or forward the exception |
| 182 AnalysisEngine.getInstance().getLogger().logError(exception); |
| 183 } |
| 184 node.setScriptElement(script); |
| 185 scripts.add(script); |
| 186 } else { |
| 187 ExternalHtmlScriptElementImpl script = new ExternalHtmlScriptElementImpl
(node); |
| 188 if (scriptSourcePath != null) { |
| 189 try { |
| 190 scriptSourcePath = UriUtilities.encode(scriptSourcePath); |
| 191 // Force an exception to be thrown if the URI is invalid so that we
can report the |
| 192 // problem. |
| 193 new URI(scriptSourcePath); |
| 194 Source scriptSource = context.getSourceFactory().resolveUri( |
| 195 htmlSource, |
| 196 scriptSourcePath); |
| 197 script.setScriptSource(scriptSource); |
| 198 if (scriptSource == null || !scriptSource.exists()) { |
| 199 reportValueError( |
| 200 HtmlWarningCode.URI_DOES_NOT_EXIST, |
| 201 scriptAttribute, |
| 202 scriptSourcePath); |
| 203 } |
| 204 } catch (URISyntaxException exception) { |
| 205 reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute, scrip
tSourcePath); |
| 206 } |
| 207 } |
| 208 node.setScriptElement(script); |
| 209 scripts.add(script); |
| 210 } |
| 211 } finally { |
| 212 parentNodes.remove(node); |
| 213 } |
| 214 return null; |
| 215 } |
| 216 |
| 217 @Override |
166 public Void visitHtmlUnit(HtmlUnit node) { | 218 public Void visitHtmlUnit(HtmlUnit node) { |
167 parentNodes = new ArrayList<XmlTagNode>(); | 219 parentNodes = new ArrayList<XmlTagNode>(); |
168 scripts = new ArrayList<HtmlScriptElement>(); | 220 scripts = new ArrayList<HtmlScriptElement>(); |
169 try { | 221 try { |
170 node.visitChildren(this); | 222 node.visitChildren(this); |
171 htmlElement.setScripts(scripts.toArray(new HtmlScriptElement[scripts.size(
)])); | 223 htmlElement.setScripts(scripts.toArray(new HtmlScriptElement[scripts.size(
)])); |
172 } finally { | 224 } finally { |
173 scripts = null; | 225 scripts = null; |
174 parentNodes = null; | 226 parentNodes = null; |
175 } | 227 } |
176 return null; | 228 return null; |
177 } | 229 } |
178 | 230 |
179 @Override | 231 @Override |
180 public Void visitXmlAttributeNode(XmlAttributeNode node) { | 232 public Void visitXmlAttributeNode(XmlAttributeNode node) { |
| 233 for (EmbeddedExpression expression : node.getExpressions()) { |
| 234 resolveExpression(expression.getExpression()); |
| 235 } |
181 return null; | 236 return null; |
182 } | 237 } |
183 | 238 |
184 @Override | 239 @Override |
185 public Void visitXmlTagNode(XmlTagNode node) { | 240 public Void visitXmlTagNode(XmlTagNode node) { |
186 if (parentNodes.contains(node)) { | 241 if (parentNodes.contains(node)) { |
187 // | 242 return reportCircularity(node); |
188 // This should not be possible, but we have an error report that suggests
that it happened at | |
189 // least once. This code will guard against infinite recursion and might h
elp us identify the | |
190 // cause of the issue. | |
191 // | |
192 StringBuilder builder = new StringBuilder(); | |
193 builder.append("Found circularity in XML nodes: "); | |
194 boolean first = true; | |
195 for (XmlTagNode pathNode : parentNodes) { | |
196 if (first) { | |
197 first = false; | |
198 } else { | |
199 builder.append(", "); | |
200 } | |
201 String tagName = pathNode.getTag().getLexeme(); | |
202 if (pathNode == node) { | |
203 builder.append("*"); | |
204 builder.append(tagName); | |
205 builder.append("*"); | |
206 } else { | |
207 builder.append(tagName); | |
208 } | |
209 } | |
210 AnalysisEngine.getInstance().getLogger().logError(builder.toString()); | |
211 return null; | |
212 } | 243 } |
213 parentNodes.add(node); | 244 parentNodes.add(node); |
214 try { | 245 try { |
215 if (isScriptNode(node)) { | 246 for (EmbeddedExpression expression : node.getExpressions()) { |
216 Source htmlSource = htmlElement.getSource(); | 247 resolveExpression(expression.getExpression()); |
217 XmlAttributeNode scriptAttribute = getScriptSourcePath(node); | |
218 String scriptSourcePath = scriptAttribute == null ? null : scriptAttribu
te.getText(); | |
219 if (node.getAttributeEnd().getType() == TokenType.GT && scriptSourcePath
== null) { | |
220 EmbeddedHtmlScriptElementImpl script = new EmbeddedHtmlScriptElementIm
pl(node); | |
221 String contents = node.getContent(); | |
222 | |
223 //TODO (danrubel): Move scanning embedded scripts into AnalysisContext
Impl | |
224 // so that clients such as builder can scan, parse, and get errors wit
hout resolving | |
225 int attributeEnd = node.getAttributeEnd().getEnd(); | |
226 Location location = lineInfo.getLocation(attributeEnd); | |
227 Scanner scanner = new Scanner( | |
228 htmlSource, | |
229 new SubSequenceReader(contents, attributeEnd), | |
230 errorListener); | |
231 scanner.setSourceStart(location.getLineNumber(), location.getColumnNum
ber()); | |
232 com.google.dart.engine.scanner.Token firstToken = scanner.tokenize(); | |
233 int[] lineStarts = scanner.getLineStarts(); | |
234 | |
235 //TODO (danrubel): Move parsing embedded scripts into AnalysisContextI
mpl | |
236 // so that clients such as builder can scan, parse, and get errors wit
hout resolving | |
237 Parser parser = new Parser(htmlSource, errorListener); | |
238 CompilationUnit unit = parser.parseCompilationUnit(firstToken); | |
239 // unit.setLineInfo(new LineInfo(lineStarts)); | |
240 | |
241 try { | |
242 LibraryResolver resolver = new LibraryResolver(context); | |
243 LibraryElementImpl library = (LibraryElementImpl) resolver.resolveEm
beddedLibrary( | |
244 htmlSource, | |
245 modificationStamp, | |
246 unit, | |
247 true); | |
248 script.setScriptLibrary(library); | |
249 resolvedLibraries.addAll(resolver.getResolvedLibraries()); | |
250 errorListener.addAll(resolver.getErrorListener()); | |
251 } catch (AnalysisException exception) { | |
252 //TODO (danrubel): Handle or forward the exception | |
253 AnalysisEngine.getInstance().getLogger().logError(exception); | |
254 } | |
255 scripts.add(script); | |
256 } else { | |
257 ExternalHtmlScriptElementImpl script = new ExternalHtmlScriptElementIm
pl(node); | |
258 if (scriptSourcePath != null) { | |
259 try { | |
260 scriptSourcePath = UriUtilities.encode(scriptSourcePath); | |
261 // Force an exception to be thrown if the URI is invalid so that w
e can report the | |
262 // problem. | |
263 new URI(scriptSourcePath); | |
264 Source scriptSource = context.getSourceFactory().resolveUri( | |
265 htmlSource, | |
266 scriptSourcePath); | |
267 script.setScriptSource(scriptSource); | |
268 if (scriptSource == null || !scriptSource.exists()) { | |
269 reportValueError( | |
270 HtmlWarningCode.URI_DOES_NOT_EXIST, | |
271 scriptAttribute, | |
272 scriptSourcePath); | |
273 } | |
274 } catch (URISyntaxException exception) { | |
275 reportValueError(HtmlWarningCode.INVALID_URI, scriptAttribute, scr
iptSourcePath); | |
276 } | |
277 } | |
278 scripts.add(script); | |
279 } | |
280 } else { | |
281 node.visitChildren(this); | |
282 } | 248 } |
| 249 node.visitChildren(this); |
283 } finally { | 250 } finally { |
284 parentNodes.remove(node); | 251 parentNodes.remove(node); |
285 } | 252 } |
286 return null; | 253 return null; |
287 } | 254 } |
288 | 255 |
289 /** | 256 /** |
290 * Return the first source attribute for the given tag node, or {@code null} i
f it does not exist. | 257 * Return the first source attribute for the given tag node, or {@code null} i
f it does not exist. |
291 * | 258 * |
292 * @param node the node containing attributes | 259 * @param node the node containing attributes |
293 * @return the source attribute contained in the given tag | 260 * @return the source attribute contained in the given tag |
294 */ | 261 */ |
295 private XmlAttributeNode getScriptSourcePath(XmlTagNode node) { | 262 private XmlAttributeNode getScriptSourcePath(XmlTagNode node) { |
296 for (XmlAttributeNode attribute : node.getAttributes()) { | 263 for (XmlAttributeNode attribute : node.getAttributes()) { |
297 if (attribute.getName().getLexeme().equals(SRC)) { | 264 if (attribute.getName().getLexeme().equals(SRC)) { |
298 return attribute; | 265 return attribute; |
299 } | 266 } |
300 } | 267 } |
301 return null; | 268 return null; |
302 } | 269 } |
303 | 270 |
| 271 private Void reportCircularity(XmlTagNode node) { |
| 272 // |
| 273 // This should not be possible, but we have an error report that suggests th
at it happened at |
| 274 // least once. This code will guard against infinite recursion and might hel
p us identify the |
| 275 // cause of the issue. |
| 276 // |
| 277 StringBuilder builder = new StringBuilder(); |
| 278 builder.append("Found circularity in XML nodes: "); |
| 279 boolean first = true; |
| 280 for (XmlTagNode pathNode : parentNodes) { |
| 281 if (first) { |
| 282 first = false; |
| 283 } else { |
| 284 builder.append(", "); |
| 285 } |
| 286 String tagName = pathNode.getTag().getLexeme(); |
| 287 if (pathNode == node) { |
| 288 builder.append("*"); |
| 289 builder.append(tagName); |
| 290 builder.append("*"); |
| 291 } else { |
| 292 builder.append(tagName); |
| 293 } |
| 294 } |
| 295 AnalysisEngine.getInstance().getLogger().logError(builder.toString()); |
| 296 return null; |
| 297 } |
| 298 |
304 /** | 299 /** |
305 * Determine if the specified node is a Dart script. | |
306 * | |
307 * @param node the node to be tested (not {@code null}) | |
308 * @return {@code true} if the node is a Dart script | |
309 */ | |
310 private boolean isScriptNode(XmlTagNode node) { | |
311 if (node.getTagNodes().size() != 0 || !node.getTag().getLexeme().equals(SCRI
PT)) { | |
312 return false; | |
313 } | |
314 for (XmlAttributeNode attribute : node.getAttributes()) { | |
315 if (attribute.getName().getLexeme().equals(TYPE)) { | |
316 Token valueToken = attribute.getValue(); | |
317 if (valueToken != null) { | |
318 String value = valueToken.getLexeme(); | |
319 if (value.equals(APPLICATION_DART_IN_DOUBLE_QUOTES) | |
320 || value.equals(APPLICATION_DART_IN_SINGLE_QUOTES)) { | |
321 return true; | |
322 } | |
323 } | |
324 } | |
325 } | |
326 return false; | |
327 } | |
328 | |
329 /** | |
330 * Report an error with the given error code at the given location. Use the gi
ven arguments to | 300 * Report an error with the given error code at the given location. Use the gi
ven arguments to |
331 * compose the error message. | 301 * compose the error message. |
332 * | 302 * |
333 * @param errorCode the error code of the error to be reported | 303 * @param errorCode the error code of the error to be reported |
334 * @param offset the offset of the first character to be highlighted | 304 * @param offset the offset of the first character to be highlighted |
335 * @param length the number of characters to be highlighted | 305 * @param length the number of characters to be highlighted |
336 * @param arguments the arguments used to compose the error message | 306 * @param arguments the arguments used to compose the error message |
337 */ | 307 */ |
338 private void reportError(ErrorCode errorCode, int offset, int length, Object..
. arguments) { | 308 private void reportError(ErrorCode errorCode, int offset, int length, Object..
. arguments) { |
339 errorListener.onError(new AnalysisError( | 309 errorListener.onError(new AnalysisError( |
(...skipping 12 matching lines...) Expand all Loading... |
352 * @param offset the offset of the first character to be highlighted | 322 * @param offset the offset of the first character to be highlighted |
353 * @param length the number of characters to be highlighted | 323 * @param length the number of characters to be highlighted |
354 * @param arguments the arguments used to compose the error message | 324 * @param arguments the arguments used to compose the error message |
355 */ | 325 */ |
356 private void reportValueError(ErrorCode errorCode, XmlAttributeNode attribute, | 326 private void reportValueError(ErrorCode errorCode, XmlAttributeNode attribute, |
357 Object... arguments) { | 327 Object... arguments) { |
358 int offset = attribute.getValue().getOffset() + 1; | 328 int offset = attribute.getValue().getOffset() + 1; |
359 int length = attribute.getValue().getLength() - 2; | 329 int length = attribute.getValue().getLength() - 2; |
360 reportError(errorCode, offset, length, arguments); | 330 reportError(errorCode, offset, length, arguments); |
361 } | 331 } |
| 332 |
| 333 private void resolveExpression(Expression expression) { |
| 334 // TODO(brianwilkerson) Implement this. We need to figure out the right cont
ext in which to |
| 335 // resolve the expression. |
| 336 } |
362 } | 337 } |
OLD | NEW |