View Javadoc
1   package io.jawk.ext;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2026 MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import java.lang.reflect.Method;
26  import java.util.Collections;
27  import java.util.LinkedHashMap;
28  import java.util.Map;
29  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
30  import io.jawk.ext.annotations.JawkFunction;
31  import io.jawk.jrt.IllegalAwkArgumentException;
32  import io.jawk.jrt.JRT;
33  import io.jawk.jrt.VariableManager;
34  import io.jawk.util.AwkSettings;
35  
36  /**
37   * Base class of various extensions.
38   * <p>
39   * Provides functionality common to most extensions,
40   * such as VM and JRT variable management, and convenience
41   * methods such as checkNumArgs() and toAwkString().
42   *
43   * @author Danny Daglas
44   */
45  public abstract class AbstractExtension implements JawkExtension {
46  
47  	private JRT jrt;
48  	private VariableManager vm;
49  	private AwkSettings settings;
50  	private Map<String, ExtensionFunction> annotatedFunctions;
51  
52  	/** {@inheritDoc} */
53  	@Override
54  	public String getExtensionName() {
55  		return getClass().getSimpleName();
56  	}
57  
58  	/** {@inheritDoc} */
59  	@Override
60  	@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "Extension needs direct access to runtime, VM and settings")
61  	public void init(VariableManager vmParam, JRT runtime, final AwkSettings conf) {
62  		this.vm = vmParam;
63  		this.jrt = runtime;
64  		this.settings = conf;
65  	}
66  
67  	/**
68  	 * Convert a Jawk variable to a Jawk string
69  	 * based on the value of the CONVFMT variable.
70  	 *
71  	 * @param obj The Jawk variable to convert to a Jawk string.
72  	 * @return A string representation of obj after CONVFMT
73  	 *         has been applied.
74  	 */
75  	protected final String toAwkString(Object obj) {
76  		return jrt.toAwkString(obj);
77  	}
78  
79  	/**
80  	 * Verifies that an exact number of arguments
81  	 * has been passed in by checking the length
82  	 * of the argument array.
83  	 *
84  	 * @param arr The arguments to check.
85  	 * @param expectedNum The expected number of arguments.
86  	 */
87  	protected static void checkNumArgs(Object[] arr, int expectedNum) {
88  		// some sanity checks on the arguments
89  		// (made into assertions so that
90  		// production code does not perform
91  		// these checks)
92  
93  		if (arr.length != expectedNum) {
94  			throw new IllegalAwkArgumentException("Expecting " + expectedNum + " arg(s), got " + arr.length);
95  		}
96  	}
97  
98  	/**
99  	 * <p>
100 	 * Getter for the field <code>jrt</code>.
101 	 * </p>
102 	 *
103 	 * @return the Runtime
104 	 */
105 	protected JRT getJrt() {
106 		return jrt;
107 	}
108 
109 	/**
110 	 * <p>
111 	 * Getter for the field <code>vm</code>.
112 	 * </p>
113 	 *
114 	 * @return the Variable Manager
115 	 */
116 	protected VariableManager getVm() {
117 		return vm;
118 	}
119 
120 	/**
121 	 * <p>
122 	 * Getter for the field <code>settings</code>.
123 	 * </p>
124 	 *
125 	 * @return the Settings
126 	 */
127 	protected AwkSettings getSettings() {
128 		return settings;
129 	}
130 
131 	private Map<String, ExtensionFunction> getAnnotatedFunctions() {
132 		if (annotatedFunctions == null) {
133 			annotatedFunctions = Collections.unmodifiableMap(scanAnnotatedFunctions());
134 		}
135 		return annotatedFunctions;
136 	}
137 
138 	private Map<String, ExtensionFunction> scanAnnotatedFunctions() {
139 		Map<String, ExtensionFunction> discovered = new LinkedHashMap<String, ExtensionFunction>();
140 		Class<? extends AbstractExtension> type = getClass();
141 		for (Method method : type.getMethods()) {
142 			JawkFunction function = method.getAnnotation(JawkFunction.class);
143 			if (function == null) {
144 				continue;
145 			}
146 			String keyword = function.value();
147 			ExtensionFunction existing = discovered.put(keyword, new ExtensionFunction(keyword, method));
148 			if (existing != null) {
149 				throw new IllegalStateException(
150 						"Duplicate @JawkFunction mapping for keyword '" + keyword + "' in " + type.getName());
151 			}
152 		}
153 		return discovered;
154 	}
155 
156 	/** {@inheritDoc} */
157 	@Override
158 	public Map<String, ExtensionFunction> getExtensionFunctions() {
159 		return getAnnotatedFunctions();
160 	}
161 }