View Javadoc
1   package org.metricshub.jawk.ext;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2025 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 org.metricshub.jawk.ext.annotations.JawkFunction;
31  import org.metricshub.jawk.jrt.IllegalAwkArgumentException;
32  import org.metricshub.jawk.jrt.JRT;
33  import org.metricshub.jawk.jrt.VariableManager;
34  import org.metricshub.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, getVm().getCONVFMT().toString(), settings.getLocale());
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  		assert arr != null;
93  		assert expectedNum >= 0;
94  
95  		if (arr.length != expectedNum) {
96  			throw new IllegalAwkArgumentException("Expecting " + expectedNum + " arg(s), got " + arr.length);
97  		}
98  	}
99  
100 	/**
101 	 * <p>
102 	 * Getter for the field <code>jrt</code>.
103 	 * </p>
104 	 *
105 	 * @return the Runtime
106 	 */
107 	protected JRT getJrt() {
108 		return jrt;
109 	}
110 
111 	/**
112 	 * <p>
113 	 * Getter for the field <code>vm</code>.
114 	 * </p>
115 	 *
116 	 * @return the Variable Manager
117 	 */
118 	protected VariableManager getVm() {
119 		return vm;
120 	}
121 
122 	/**
123 	 * <p>
124 	 * Getter for the field <code>settings</code>.
125 	 * </p>
126 	 *
127 	 * @return the Settings
128 	 */
129 	protected AwkSettings getSettings() {
130 		return settings;
131 	}
132 
133 	private Map<String, ExtensionFunction> getAnnotatedFunctions() {
134 		if (annotatedFunctions == null) {
135 			annotatedFunctions = Collections.unmodifiableMap(scanAnnotatedFunctions());
136 		}
137 		return annotatedFunctions;
138 	}
139 
140 	private Map<String, ExtensionFunction> scanAnnotatedFunctions() {
141 		Map<String, ExtensionFunction> discovered = new LinkedHashMap<String, ExtensionFunction>();
142 		Class<? extends AbstractExtension> type = getClass();
143 		for (Method method : type.getMethods()) {
144 			JawkFunction function = method.getAnnotation(JawkFunction.class);
145 			if (function == null) {
146 				continue;
147 			}
148 			String keyword = function.value();
149 			ExtensionFunction existing = discovered.put(keyword, new ExtensionFunction(keyword, method));
150 			if (existing != null) {
151 				throw new IllegalStateException(
152 						"Duplicate @JawkFunction mapping for keyword '" + keyword + "' in " + type.getName());
153 			}
154 		}
155 		return discovered;
156 	}
157 
158 	/** {@inheritDoc} */
159 	@Override
160 	public Map<String, ExtensionFunction> getExtensionFunctions() {
161 		return getAnnotatedFunctions();
162 	}
163 }