// Copyright (C) 2010 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.lexer.TokenConsumer;
import com.google.caja.parser.AbstractParseTreeNode;
import com.google.caja.parser.ParserBase;
import com.google.caja.render.Concatenator;
import com.google.caja.render.JsPrettyPrinter;
import com.google.caja.reporting.RenderContext;
import com.google.caja.util.Callback;
import java.io.IOException;
import java.util.List;
/**
* A property name and definitions associated with one property in an object.
* <code>{ a: b, c: d }</code> has two properties.
* A single {@code ObjProperty} may not uniquely define the value of a property
* in an object ; an object may validly have two with the same name, e.g. a
* getter and a setter.
*
* @author mikesamuel@gmail.com
*/
public abstract class ObjProperty extends AbstractParseTreeNode
implements JsonMLCompatible {
private static final long serialVersionUID = 3384973990326624609L;
public ObjProperty(StringLiteral name, Expression value) {
this(FilePosition.span(name.getFilePosition(), value.getFilePosition()),
name, value);
}
public ObjProperty(FilePosition pos, StringLiteral name, Expression value) {
super(pos, Expression.class);
this.createMutation().appendChild(name).appendChild(value).execute();
}
/**
* Provided for reflection.
* @param value unused
*/
protected ObjProperty(
FilePosition pos, Void value, List<? extends Expression> children) {
this(pos, (StringLiteral) children.get(0), children.get(1));
assert children.size() == 2;
}
@Override public final Object getValue() { return null; }
@Override
public List<? extends Expression> children() {
return childrenAs(Expression.class);
}
public final StringLiteral getPropertyNameNode() {
return (StringLiteral) children().get(0);
}
@Override
public void childrenChanged() {
super.childrenChanged();
if (2 != children().size()) { throw new IndexOutOfBoundsException(); }
getPropertyName();
}
public final String getPropertyName() {
return ((StringLiteral) children().get(0)).getUnquotedValue();
}
public boolean isPropertyNameQuoted() {
StringLiteral name = (StringLiteral) children().get(0);
String rawName = name.getValue();
if (rawName.length() == 0) { return false; }
char ch0 = rawName.charAt(0);
return ch0 == '"' || ch0 == '\'';
}
public final TokenConsumer makeRenderer(
Appendable out, Callback<IOException> handler) {
return new JsPrettyPrinter(new Concatenator(out, handler));
}
protected final void renderPropertyName(
RenderContext rc, boolean preferUnquoted) {
StringLiteral key = (StringLiteral) children().get(0);
TokenConsumer out = rc.getOut();
boolean unquoted = preferUnquoted;
switch (rc.propertyNameQuotingMode()) {
case NO_QUOTES: unquoted = true; break;
case PRESERVE_QUOTES:
String name = key.getValue();
if (name.length() == 0) {
unquoted = true;
} else {
char ch = name.charAt(0);
if (ch != '"' && ch != '\'') {
unquoted = true;
}
}
break;
default: break;
}
if (unquoted || preferUnquoted) {
String uqVal = key.getUnquotedValue();
if (ParserBase.isJavascriptIdentifier(uqVal)
&& !("get".equals(uqVal) || "set".equals(uqVal))) {
out.mark(key.getFilePosition());
out.consume(uqVal);
return;
}
}
key.render(rc);
}
}