// Copyright (C) 2008 Google Inc.
//
// 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.google.caja.parser.js;
import com.google.caja.lexer.FilePosition;
import com.google.caja.parser.ParseTreeNode;
import com.google.caja.reporting.RenderContext;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* A node which can appear at the beginning of a program or function body to
* place restrictions on code that follows.
*
* <p>The {@code DirectivePrologue} of a JavaScript {@code Program} or
* {@code FunctionBody} is a sequence of zero or more
* {@code ExpressionStatement}s each of which consists entirely of a
* {@code StringLiteral}. For example:
*
* <pre>
* 'use strict';
* <em>Remainder of the program...</em>
* </pre>
*
* <p>or:
*
* <pre>
* function foo() {
* 'use strict';
* <em>Remainder of the function body...</em>
* }
* </pre>
*
* <p>For more details, see Section 14.1 of
* <a href="http://wiki.ecmascript.org/lib/exe/fetch.php?id=es3.1%3Aes3.1_proposal_working_draft&cache=cache&media=es3.1:es5-tc392008-040.pdf">the ES5 spec</a>.
*
* @author mikesamuel@gmail.com
*/
public final class DirectivePrologue extends AbstractStatement {
private static final long serialVersionUID = 2485949503702983868L;
/** @param value unused. This ctor is provided for reflection. */
@ReflectiveCtor
public DirectivePrologue(
FilePosition pos, Void value, List<? extends Directive> children) {
this(pos, children);
}
public DirectivePrologue(
FilePosition pos, List<? extends Directive> children) {
super(pos, Directive.class);
createMutation().appendChildren(children).execute();
}
@Override
protected void childrenChanged() {
super.childrenChanged();
if (children().isEmpty()) { throw new IndexOutOfBoundsException(); }
for (ParseTreeNode us : children()) {
if (!(us instanceof Directive)) {
throw new ClassCastException(us.getClass().getName());
}
}
}
@Override
public Object getValue() { return null; }
@Override
public List<? extends Directive> children() {
return childrenAs(Directive.class);
}
public boolean hasDirective(String directive) {
return getDirectives().contains(directive);
}
public Set<String> getDirectives() {
Set<String> directives = new LinkedHashSet<String>();
for (Directive us : children()) {
directives.add(us.getDirectiveString());
}
return Collections.unmodifiableSet(directives);
}
public void render(RenderContext rc) {
for (Directive d : children()) { d.render(rc); }
}
@Override
public boolean isTerminal() {
return true;
}
public boolean hasHangingConditional() { return false; }
}