aboutsummaryrefslogtreecommitdiffstats
path: root/configgen/src/main/java/com/yahoo/config/codegen/MakeConfig.java
blob: 0cc10dc404bd9cf504aede4f990a5ef08f7dc4aa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.codegen;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;

/**
 * This class generates code for a config class from a given def-file.
 */
public class MakeConfig {

    private final ClassBuilder classBuilder;

    public MakeConfig(InnerCNode root, NormalizedDefinition nd, MakeConfigProperties properties) {
        classBuilder = createClassBuilder(root, nd, properties);
    }

    private static ClassBuilder createClassBuilder(InnerCNode root, NormalizedDefinition nd, MakeConfigProperties properties) {
        if (isCpp(properties))
            return new CppClassBuilder(root, nd, properties.destDir, properties.dirInRoot);
        else
            return new JavaClassBuilder(root, nd, properties.destDir, properties.javaPackagePrefix);
    }

    @SuppressWarnings("WeakerAccess") // Used by ConfigGenMojo
    public static boolean makeConfig(MakeConfigProperties properties) throws FileNotFoundException {
        for (File specFile : properties.specFiles) {
            String name = specFile.getName();
            if (name.endsWith(".def")) name = name.substring(0, name.length() - 4);

            DefParser parser = new DefParser(name, new FileReader(specFile));
            parser.enableSystemErr();
            InnerCNode configRoot = parser.getTree();
            checkNamespaceAndPacakge(name, configRoot, isCpp(properties));

            if (configRoot != null) {
                MakeConfig mc = new MakeConfig(configRoot, parser.getNormalizedDefinition(), properties);
                mc.buildClasses();
                if (properties.dumpTree) {
                    System.out.println("\nTree dump:");
                    DefParser.dumpTree(configRoot, "");
                }
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * Generates the code and print it to this.out.
     */
    private void buildClasses() {
        classBuilder.createConfigClasses();
    }

    private static void printUsage(PrintStream out) {
        out.println("Usage: java -Dconfig.dest=<dir> -Dconfig.spec=<path> [-Dconfig.lang=cpp -Dconfig.subdir=<dir>] [-Dconfig.dumpTree=true] MakeConfig");
        out.println("       (default language for generated code is Java)");
    }

    public static void main(String[] args) throws IOException {
        try {
            MakeConfigProperties props = new MakeConfigProperties();
            boolean success = makeConfig(props);
            if (!success) System.exit(1);
        } catch (PropertyException e) {
            System.out.println(Exceptions.toMessageString(e));
            printUsage(System.err);
            System.exit(1);
        } catch (CodegenRuntimeException e) {
            System.out.println(Exceptions.toMessageString(e));
            System.exit(1);
        }
    }

   private static void checkNamespaceAndPacakge(String name, InnerCNode configRoot, boolean isCpp) {
        if (isCpp && configRoot.defNamespace == null)
            throw new IllegalArgumentException("In config definition '" + name + "': A namespace is required");
       if (configRoot.defNamespace == null && configRoot.defPackage == null)
           throw new IllegalArgumentException("In config definition '" + name + "': A package (or namespace) is required");
    }

    private static boolean isCpp(MakeConfigProperties properties) {
        return properties.language.equals("cpp");
    }

    // The Exceptions class below is copied from vespajlib/com.yahoo.protect.Exceptions

    /**
     * Helper methods for handling exceptions
     *
     * @author bratseth
     */
    static class Exceptions {

        /**
         * Returns a use friendly error message string which includes information from all nested exceptions.
         *
         * The form of this string is
         * <code>e.getMessage(): e.getCause().getMessage(): e.getCause().getCause().getMessage()...</code>
         * In addition, some heuristics are used to clean up common cases where exception nesting causes bad messages.
         */
        static String toMessageString(Throwable t) {
            StringBuilder b = new StringBuilder();
            String lastMessage = null;
            String message;
            for (; t != null; t = t.getCause(), lastMessage = message) {
                message = getMessage(t);
                if (message == null) continue;
                if (lastMessage != null && lastMessage.equals(message)) continue;
                if (b.length() > 0)
                    b.append(": ");
                b.append(message);
            }
            return b.toString();
        }

        /**
         * Returns a useful message from *this* exception, or null if none
         */
        private static String getMessage(Throwable t) {
            String message = t.getMessage();
            if (t.getCause() == null) {
                if (message == null) return toShortClassName(t);
                return message;
            } else {
                if (message == null) return null;
                if (message.equals(t.getCause().getClass().getName() + ": " + t.getCause().getMessage())) return null;
                return message;
            }
        }

        private static String toShortClassName(Object o) {
            String longName = o.getClass().getName();
            int lastDot = longName.lastIndexOf(".");
            if (lastDot < 0) return longName;
            return longName.substring(lastDot + 1);
        }
    }

}