1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.incubator.code.extern;
27
28 import jdk.incubator.code.Op;
29
30 /**
31 * An operation factory for constructing an {@link Op operation} from its
32 * {@link ExternalizedOp external content}.
33 */
34 @FunctionalInterface
35 public interface OpFactory {
36
37 /**
38 * Constructs an {@link Op operation} from its external content.
39 * <p>
40 * If there is no mapping from the operation's name to a concrete
41 * class of an {@code Op} then this method returns null.
42 *
43 * @param def the operation's external content
44 * @return the operation, otherwise null
45 */
46 Op constructOp(ExternalizedOp def);
47
48 /**
49 * Constructs an {@link Op operation} from its external content.
50 * <p>
51 * If there is no mapping from the operation's name to a concrete
52 * class of an {@code Op} then this method throws UnsupportedOperationException.
53 *
54 * @param def the operation's external content
55 * @return the operation
56 * @throws UnsupportedOperationException if there is no mapping from the operation's
57 * name to a concrete class of an {@code Op}
58 */
59 default Op constructOpOrFail(ExternalizedOp def) {
60 Op op = constructOp(def);
61 if (op == null) {
62 throw new UnsupportedOperationException("Unsupported operation: " + def.name());
63 }
64
65 return op;
66 }
67
68 /**
69 * Composes this operation factory with another operation factory.
70 * <p>
71 * If there is no mapping in this operation factory then the result
72 * of the other operation factory is returned.
73 *
74 * @param after the other operation factory.
75 * @return the composed operation factory.
76 */
77 default OpFactory andThen(OpFactory after) {
78 return def -> {
79 Op op = constructOp(def);
80 return op != null ? op : after.constructOp(def);
81 };
82 }
83
84 // // Uncomment the following and execute using an exploded build like as follows to generate a factory method
85 // // for enclosed concrete operations
86 // // java --add-modules jdk.incubator.code jdk.incubator.code.extern.OpFactory jdk.incubator.code.dialect.core.CoreOp
87 // static void main(String[] args) {
88 // Class<?> enclosingOpClass = null;
89 // try {
90 // enclosingOpClass = Class.forName(args[0]);
91 // } catch (ClassNotFoundException e) {
92 // throw new RuntimeException(e);
93 // }
94 // generateSwitchExpression(enclosingOpClass, System.out);
95 // }
96 //
97 // static void generateSwitchExpression(Class<?> enclosingOpClass, java.io.PrintStream out) {
98 // java.util.Map<String, java.lang.reflect.Executable> opNameMap = new java.util.TreeMap<>();
99 // for (Class<?> opClass : enclosingOpClass.getNestMembers()) {
100 // if (!Op.class.isAssignableFrom(opClass)) {
101 // continue;
102 // }
103 // if (!java.lang.reflect.Modifier.isFinal(opClass.getModifiers())) {
104 // continue;
105 // }
106 //
107 // var opDecl = opClass.getAnnotation(jdk.incubator.code.internal.OpDeclaration.class);
108 // String name = opDecl.value();
109 //
110 // var e = getOpConstructorExecutable(opClass);
111 // opNameMap.put(name, e);
112 // }
113 //
114 // out.println("static Op createOp(ExternalizedOp def) {");
115 // out.println(" Op op = switch (def.name()) {");
116 // opNameMap.forEach((name, e) -> {
117 // out.print(" case \"" + name + "\" -> ");
118 // switch (e) {
119 // case java.lang.reflect.Constructor<?> constructor -> {
120 // out.println("new " + name(enclosingOpClass, constructor.getDeclaringClass()) + "(def);");
121 // }
122 // case java.lang.reflect.Method method -> {
123 // out.println(name(enclosingOpClass, method.getDeclaringClass()) + "." + method.getName() + "(def);");
124 // }
125 // }
126 // });
127 // out.println(" default -> null;");
128 // out.println(" };");
129 // out.print(
130 // """
131 // if (op != null) {
132 // op.setLocation(def.location());
133 // }
134 // return op;
135 // """);
136 // out.println("}");
137 // }
138 //
139 // private static java.lang.reflect.Executable getOpConstructorExecutable(Class<?> opClass) {
140 // java.lang.reflect.Executable e = null;
141 // try {
142 // e = opClass.getDeclaredMethod("create", ExternalizedOp.class);
143 // } catch (NoSuchMethodException _) {
144 // }
145 //
146 // if (e != null) {
147 // if (!java.lang.reflect.Modifier.isStatic(e.getModifiers())) {
148 // throw new InternalError("Operation constructor is not a static method: " + e);
149 // }
150 // return e;
151 // }
152 //
153 // try {
154 // e = opClass.getDeclaredConstructor(ExternalizedOp.class);
155 // } catch (NoSuchMethodException _) {
156 // return null;
157 // }
158 //
159 // return e;
160 // }
161 //
162 // static String name(Class<?> enclosingOpClass, Class<?> declaringClass) {
163 // return declaringClass.getCanonicalName().substring(enclosingOpClass.getCanonicalName().length() + 1);
164 // }
165 }