/* * Copyright 2011 Google Inc. All Rights Reserved. * * 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.devtools.j2objc.gen; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; import com.google.devtools.j2objc.types.Import; import com.google.devtools.j2objc.util.ErrorUtil; import com.google.devtools.j2objc.util.UnicodeUtils; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Generates source files from AST types. This class handles common actions * shared by the header and implementation generators. * * @author Tom Ball */ public abstract class ObjectiveCSourceFileGenerator extends AbstractSourceGenerator { private final GenerationUnit unit; private final Map<String, GeneratedType> typesByName; private final List<GeneratedType> orderedTypes; /** * Create a new generator. * * @param unit The AST of the source to generate * @param emitLineDirectives if true, generate CPP line directives */ protected ObjectiveCSourceFileGenerator(GenerationUnit unit, boolean emitLineDirectives) { super(new SourceBuilder(emitLineDirectives)); this.unit = unit; orderedTypes = getOrderedGeneratedTypes(unit); typesByName = Maps.newHashMap(); for (GeneratedType type : orderedTypes) { String name = type.getTypeName(); if (name != null) { typesByName.put(name, type); } } } /** * Returns the suffix for files created by this generator. */ protected abstract String getSuffix(); protected String getOutputPath() { return getGenerationUnit().getOutputPath() + getSuffix(); } protected GenerationUnit getGenerationUnit() { return unit; } protected List<GeneratedType> getOrderedTypes() { return orderedTypes; } protected GeneratedType getLocalType(String name) { return typesByName.get(name); } protected boolean isLocalType(String name) { return typesByName.containsKey(name); } protected void save(String path) { try { File outputDirectory = unit.options().fileUtil().getOutputDirectory(); File outputFile = new File(outputDirectory, path); File dir = outputFile.getParentFile(); if (dir != null && !dir.exists()) { if (!dir.mkdirs()) { ErrorUtil.warning("cannot create output directory: " + outputDirectory); } } String source = getBuilder().toString(); // Make sure file ends with a new-line. if (!source.endsWith("\n")) { source += '\n'; } Files.write(source, outputFile, unit.options().fileUtil().getCharset()); } catch (IOException e) { ErrorUtil.error(e.getMessage()); } finally { reset(); } } /** Ignores deprecation warnings. Deprecation warnings should be visible for human authored code, * not transpiled code. This method should be paired with popIgnoreDeprecatedDeclarationsPragma. */ protected void pushIgnoreDeprecatedDeclarationsPragma() { if (unit.options().generateDeprecatedDeclarations()) { newline(); println("#pragma clang diagnostic push"); println("#pragma GCC diagnostic ignored \"-Wdeprecated-declarations\""); } } /** Restores deprecation warnings after a call to pushIgnoreDeprecatedDeclarationsPragma. */ protected void popIgnoreDeprecatedDeclarationsPragma() { if (unit.options().generateDeprecatedDeclarations()) { println("\n#pragma clang diagnostic pop"); } } protected void printForwardDeclarations(Set<Import> forwardDecls) { Set<String> forwardStmts = Sets.newTreeSet(); for (Import imp : forwardDecls) { forwardStmts.add(createForwardDeclaration(imp.getTypeName(), imp.isInterface())); } if (!forwardStmts.isEmpty()) { newline(); for (String stmt : forwardStmts) { println(stmt); } } } private String createForwardDeclaration(String typeName, boolean isInterface) { return UnicodeUtils.format("@%s %s;", isInterface ? "protocol" : "class", typeName); } private static List<GeneratedType> getOrderedGeneratedTypes(GenerationUnit generationUnit) { // Ordered map because we iterate over it below. Collection<GeneratedType> generatedTypes = generationUnit.getGeneratedTypes(); LinkedHashMap<String, GeneratedType> typeMap = Maps.newLinkedHashMap(); for (GeneratedType generatedType : generatedTypes) { String name = generatedType.getTypeName(); if (name != null) { Object dupe = typeMap.put(name, generatedType); assert dupe == null : "Duplicate type name: " + name; } } LinkedHashSet<GeneratedType> orderedTypes = Sets.newLinkedHashSet(); for (GeneratedType generatedType : generatedTypes) { collectType(generatedType, orderedTypes, typeMap); } return Lists.newArrayList(orderedTypes); } private static void collectType( GeneratedType generatedType, LinkedHashSet<GeneratedType> orderedTypes, Map<String, GeneratedType> typeMap) { for (String superType : generatedType.getSuperTypes()) { GeneratedType requiredType = typeMap.get(superType); if (requiredType != null) { collectType(requiredType, orderedTypes, typeMap); } } orderedTypes.add(generatedType); } }