/* * 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); }