blob: c1779546c77004fc359788a5605b0c9f66530b65 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.nativeexecution.api.util;
import java.io.IOException;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.swing.SwingUtilities;
import org.netbeans.modules.nativeexecution.api.util.ConnectionManager.CancellationException;
import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment;
import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory;
import org.netbeans.modules.nativeexecution.api.HostInfo;
import org.netbeans.modules.nativeexecution.api.HostInfo.OS;
public final class MacroExpanderFactory {
private static final Map<String, MacroExpander> expanderCache = new ConcurrentHashMap<>();
public static enum ExpanderStyle {
SUNSTUDIO_STYLE,
DEFAULT_STYLE
}
private MacroExpanderFactory() {
}
public static MacroExpander getExpander(ExecutionEnvironment execEnv) {
return getExpander(execEnv, ExpanderStyle.DEFAULT_STYLE, true);
}
public static MacroExpander getExpander(ExecutionEnvironment execEnv, boolean connectIfNeed) {
return getExpander(execEnv, ExpanderStyle.DEFAULT_STYLE, connectIfNeed);
}
public static MacroExpander getExpander(
ExecutionEnvironment execEnv, ExpanderStyle style) {
return getExpander(execEnv, style, true);
}
public static MacroExpander getExpander(
ExecutionEnvironment execEnv, ExpanderStyle style, boolean connectIfNeed) {
if (connectIfNeed && !HostInfoUtils.isHostInfoAvailable(execEnv)) {
if (SwingUtilities.isEventDispatchThread()) {
// otherwuse we'll hang forever in the attempt to connect
throw new IllegalThreadStateException("Should never be called from AWT thread"); // NOI18N
}
}
String key = ExecutionEnvironmentFactory.toUniqueID(execEnv) + '_' + style;
MacroExpander result = expanderCache.get(key);
if (result != null) {
return result;
}
HostInfo hostInfo = null;
try {
if (connectIfNeed || HostInfoUtils.isHostInfoAvailable(execEnv)) {
hostInfo = HostInfoUtils.getHostInfo(execEnv);
}
} catch (IOException ex) {
// ideally, the method should throw IOException,
// but it's to dangerous to change signature right now
ex.printStackTrace(System.err);
} catch (CancellationException ex) {
// ideally, the method should throw CancellationException,
// but it's to dangerous to change signature right now
}
result = new MacroExpanderImpl(hostInfo, style);
if (hostInfo != null) {
MacroExpander existing = expanderCache.putIfAbsent(key, result);
if (existing != null) {
result = existing;
}
}
return result;
}
public interface MacroExpander {
public String expandPredefinedMacros(String string) throws ParseException;
public String expandMacros(
String string,
Map<String, String> envVariables) throws ParseException;
}
private static class MacroExpanderImpl implements MacroExpander {
private static final int[][] ttable = new int[][]{
{0, 0, 0, 1, 0, 0},
{2, 3, 3, 10, 4, 3},
{2, 2, 5, 6, 5, 5},
{7, 7, 8, 8, 8, 9},
{7, 3, 3, 3, 8, 8}
};
protected final Map<String, String> predefinedMacros =
Collections.synchronizedMap(new HashMap<String, String>());
protected final HostInfo hostInfo;
public MacroExpanderImpl(final HostInfo hostInfo, final ExpanderStyle style) {
this.hostInfo = hostInfo;
setupPredefined(style);
}
private int getCharClass(char c) {
if (c == '_' || (c >= 'A' && c <= 'Z') || c >= 'a' && c <= 'z') {
return 0;
}
if (c >= '0' && c <= '9') {
return 1;
}
if (c == '$') {
return 3;
}
if (c == '{') {
return 4;
}
if (c == '}') {
return 5;
}
return 2;
}
private String valueOf(String macro, Map<String, String> map) {
String result = map.get(macro);
return result == null ? "${" + macro + "}" : result; // NOI18N
}
@Override
public final String expandPredefinedMacros(
final String string) throws ParseException {
return expandMacros(string, predefinedMacros);
}
@Override
public final String expandMacros(
final String string,
final Map<String, String> map) throws ParseException {
if (string == null || string.length() == 0) {
return string;
}
StringBuilder res = new StringBuilder();
StringBuilder buf = new StringBuilder();
int state = 0, pos = 0, mpos = -1;
char[] chars = (string + (char) 0).toCharArray();
char c;
while (pos < chars.length) {
c = chars[pos];
switch (ttable[state][getCharClass(c)]) {
case 0:
if (c != 0) {
res.append(c);
}
break;
case 1:
mpos = pos;
buf.setLength(0);
state = 1;
break;
case 2:
buf.append(c);
state = 2;
break;
case 3:
res.append(string.substring(mpos, pos + (c == 0 ? 0 : 1)));
buf.setLength(0);
state = 0;
break;
case 4:
state = 4;
break;
case 5:
res.append(valueOf(buf.toString().trim(), map));
pos--;
buf.setLength(0);
state = 0;
break;
case 6:
res.append(valueOf(buf.toString().trim(), map));
mpos = pos;
buf.setLength(0);
state = 1;
break;
case 7:
buf.append(c);
state = 3;
break;
case 8:
throw new ParseException("Bad substitution", pos); // NOI18N
case 9:
res.append(valueOf(buf.toString().trim(), map));
buf.setLength(0);
state = 0;
break;
case 10:
res.append(string.substring(mpos, pos));
pos--;
buf.setLength(0);
state = 0;
break;
}
pos++;
}
return res.toString();
}
protected final void setupPredefined(ExpanderStyle style) {
if (hostInfo == null) {
return;
}
String soext;
String osname;
switch (hostInfo.getOSFamily()) {
case WINDOWS:
soext = "dll"; // NOI18N
osname = "Windows"; // NOI18N
break;
case MACOSX:
soext = "dylib"; // NOI18N
osname = "MacOSX"; // NOI18N
break;
case SUNOS:
soext = "so"; // NOI18N
osname = "SunOS"; // NOI18N
break;
case LINUX:
soext = "so"; // NOI18N
osname = "Linux"; // NOI18N
break;
case FREEBSD:
soext = "so"; // NOI18N
osname = "FreeBSD"; // NOI18N
break;
default:
osname = hostInfo.getOSFamily().name();
soext = "so"; // NOI18N
}
OS os = hostInfo.getOS();
predefinedMacros.put("hostname", hostInfo.getHostname().toLowerCase()); // NOI18N
predefinedMacros.put("soext", soext); // NOI18N
predefinedMacros.put("osname", osname); // NOI18N
predefinedMacros.put("isa", os.getBitness().toString()); // NOI18N
predefinedMacros.put("_isa", os.getBitness() == HostInfo.Bitness._64 && hostInfo.getCpuFamily() != HostInfo.CpuFamily.AARCH64 ? "_64" : ""); // NOI18N
String platform = hostInfo.getCpuFamily().name().toLowerCase();
if (style == ExpanderStyle.SUNSTUDIO_STYLE) {
if ("x86".equals(platform)) { // NOI18N
platform = "intel"; // NOI18N
}
if (hostInfo.getOSFamily() == HostInfo.OSFamily.SUNOS) { // NOI18N
platform += "-S2"; // NOI18N
} else {
platform += "-" + osname; // NOI18N
}
}
predefinedMacros.put("platform", platform); // NOI18N
}
}
}