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 }