/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.codegen;
import org.jboss.errai.codegen.literal.LiteralValue;
/**
* An <tt>InternCallback</tt> can be registered with {@link Context#addInterningCallback(InterningCallback)}.
* <p>
* Some care should be taken in implementing a callback considering the recursive nature of code generation
* within the framework. For instance, the following code will produce undesirable results:
* {code}
* new InterningCallback() {
* public Statement intern(LiteralValue<?> literalValue) {
* if (literalValue instanceof StringLiteral) {
* final String varName = "stringLiteral_" + literalValue.getValue().hashCode();
* getClassBuilder().publicField(varName, String.class)
* .initializesWith(literalValue.getValue());
*
* return Refs.get(varName);
* }
* return null;
* }
* }
* {code}
* On the surface, the above seems like a reasonable enough implementation. But there is a recursion problem
* hidden in it. Because we initialize the field with the string value in the default manner, the value will
* be obtained from the {@link org.jboss.errai.codegen.literal.LiteralFactory} in the regular manner, and the
* interned value will reference itself!
* <p>
* You'll end up with code which looks like this:
* {code}
* stringLiteral_382389210 = stringLiteral_382389210;
* {code}
* ... And it's fair to say the compiler will not like this.
* <p>
* Instead, you should create non-recursive constructs. We can fix the above code like so:
* {code}
* new InterningCallback() {
* public Statement intern(final LiteralValue<?> literalValue) {
* if (literalValue instanceof StringLiteral) {
* final String varName = "stringLiteral_" + literalValue.getValue().hashCode();
* getClassBuilder().publicField(varName, String.class)
* .initializesWith(
* new Statement() {
* public String generate(Context context) {
* return new StringLiteral(literalValue.getValue()).getCanonicalString(context);
* }
*
* public MetaClass getType() {
* return literalValue.getType();
* }
* }
* );
*
* return Refs.get(varName);
* }
* return null;
* }
* }
* {code}
*
* @author Mike Brock
*/
public interface InterningCallback {
/**
* Intern the supplied {@link LiteralValue}. This interface allows you to implement an interning strategy for
* literal values within the code generator framework. For instance, having literalized annotations render
* to a final field within a generated class with all subsequent references to matching annotations reference
* that field.
*
* @param literalValue the literal value to intern.
*
* @return If this method returns a non-null reference,the generator will assume this value is interned and will
* use the returned {@link Statement} for this literal in all future code generation.
*/
public Statement intern(LiteralValue<?> literalValue);
}