Skip to content

Commit 6ec84d5

Browse files
committed
Fix Spring version variable name discoverer compatiblity
1 parent 936e192 commit 6ec84d5

File tree

2 files changed

+98
-50
lines changed

2 files changed

+98
-50
lines changed

repository/src/main/java/org/orderofthebee/addons/support/tools/repo/jsconsole/AlfrescoScriptAPITernGet.java

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@
7777
import org.springframework.beans.factory.DisposableBean;
7878
import org.springframework.beans.factory.InitializingBean;
7979
import org.springframework.context.ApplicationContextAware;
80-
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
8180
import org.springframework.core.ParameterNameDiscoverer;
8281
import org.springframework.extensions.surf.util.I18NUtil;
8382
import org.springframework.extensions.webscripts.Cache;
@@ -94,6 +93,8 @@
9493
public class AlfrescoScriptAPITernGet extends DeclarativeWebScript implements InitializingBean
9594
{
9695

96+
private static final Logger LOGGER = LoggerFactory.getLogger(AlfrescoScriptAPITernGet.class);
97+
9798
private static final Collection<Class<?>> PRIMITIVE_NUMBER_CLASSES = Collections
9899
.unmodifiableList(Arrays.<Class<?>> asList(byte.class, short.class, int.class, long.class, float.class, double.class));
99100

@@ -108,9 +109,42 @@ public class AlfrescoScriptAPITernGet extends DeclarativeWebScript implements In
108109
private static final Collection<String> INIT_METHOD_NAMES = Collections
109110
.unmodifiableSet(new HashSet<String>(Arrays.<String> asList("init", "register")));
110111

111-
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
112+
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER;
113+
static
114+
{
115+
// Alfresco 5.0.d (earliest version we want to try to support) contains Spring 3.x
116+
// StandardReflectionParameterNameDiscoverer is only available from Spring 4.x on
117+
Class<?> cls = null;
118+
try
119+
{
120+
cls = Class.forName("org.springframework.core.StandardReflectionParameterNameDiscoverer");
121+
}
122+
catch (final ClassNotFoundException ex)
123+
{
124+
try
125+
{
126+
cls = Class.forName("org.springframework.core.LocalVariableTableParameterNameDiscoverer");
127+
}
128+
catch (final ClassNotFoundException ex2)
129+
{
130+
LOGGER.info("No valid Spring parameter name discoverer class found");
131+
}
132+
}
112133

113-
private static final Logger LOGGER = LoggerFactory.getLogger(AlfrescoScriptAPITernGet.class);
134+
ParameterNameDiscoverer pnd = null;
135+
if (cls != null)
136+
{
137+
try
138+
{
139+
pnd = (ParameterNameDiscoverer) cls.newInstance();
140+
}
141+
catch (final InstantiationException | IllegalAccessException ex)
142+
{
143+
LOGGER.warn("Failed to instantiate Spring paramater name discoverer", ex);
144+
}
145+
}
146+
PARAMETER_NAME_DISCOVERER = pnd;
147+
}
114148

115149
protected NamespaceService namespaceService;
116150

@@ -320,7 +354,7 @@ protected List<Map<String, Object>> prepareJavaTypeDefinitions(final Map<String,
320354

321355
while (classesToDescribe.size() > classesDescribed.size())
322356
{
323-
final Collection<Class<?>> remainingClasses = new HashSet<Class<?>>(classesToDescribe);
357+
final Collection<Class<?>> remainingClasses = new HashSet<>(classesToDescribe);
324358
remainingClasses.removeAll(classesDescribed);
325359

326360
for (final Class<?> cls : remainingClasses)
@@ -333,7 +367,7 @@ protected List<Map<String, Object>> prepareJavaTypeDefinitions(final Map<String,
333367

334368
if (skip == null || skip.isEmpty() || !Boolean.parseBoolean(skip))
335369
{
336-
final Map<String, Object> typeDefinition = new HashMap<String, Object>();
370+
final Map<String, Object> typeDefinition = new HashMap<>();
337371
final Collection<Class<?>> relatedClasses = this.fillClassTypeDefinition(cls, typeDefinition);
338372
classesToDescribe.addAll(relatedClasses);
339373
typeDefinitions.add(typeDefinition);
@@ -401,7 +435,7 @@ protected List<Map<String, Object>> prepareGlobalDefinitions(final Map<String, O
401435
}
402436
}
403437

404-
final Collection<Class<?>> dummyClasses = new HashSet<Class<?>>();
438+
final Collection<Class<?>> dummyClasses = new HashSet<>();
405439

406440
for (final Entry<String, Object> globalEntry : model.entrySet())
407441
{
@@ -509,18 +543,18 @@ protected Collection<Class<?>> fillClassTypeDefinition(final Class<?> cls, final
509543
final String nameOnly = this.properties.getProperty(commonPrefix + ".nameOnly");
510544
if (nameOnly == null || nameOnly.isEmpty() || !Boolean.parseBoolean(nameOnly))
511545
{
512-
this.fillClassTypeMemberDefinitions(cls, typeDefinition, relatedClasses, commonPrefix, superclass);
546+
this.fillClassTypeMemberDefinitions(cls, typeDefinition, relatedClasses, commonPrefix);
513547
}
514548

515549
return relatedClasses;
516550
}
517551

518552
protected void fillClassTypeMemberDefinitions(final Class<?> cls, final Map<String, Object> typeDefinition,
519-
final Collection<Class<?>> relatedClasses, final String commonPrefix, final Class<?> superclass)
553+
final Collection<Class<?>> relatedClasses, final String commonPrefix)
520554
{
521-
final List<Map<String, Object>> memberDefinitions = new ArrayList<Map<String, Object>>();
522-
final Map<String, AtomicInteger> usedMemberNames = new HashMap<String, AtomicInteger>();
523-
final Collection<String> handledProperties = new HashSet<String>();
555+
final List<Map<String, Object>> memberDefinitions = new ArrayList<>();
556+
final Map<String, AtomicInteger> usedMemberNames = new HashMap<>();
557+
final Collection<String> handledProperties = new HashSet<>();
524558

525559
final List<Method> methods = this.collectDocumentableMethods(cls);
526560

@@ -1014,7 +1048,7 @@ protected String buildMethodTypeDescription(final Method method, final Class<?>
10141048
typeBuilder.append("fn(");
10151049
if (parameterTypes.length > 0)
10161050
{
1017-
final String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
1051+
final String[] parameterNames = PARAMETER_NAME_DISCOVERER != null ? PARAMETER_NAME_DISCOVERER.getParameterNames(method) : null;
10181052

10191053
for (int idx = 0; idx < parameterTypes.length; idx++)
10201054
{
@@ -1047,7 +1081,12 @@ else if (Boolean.class.equals(parameterTypes[idx]))
10471081
typeName = "bool";
10481082
}
10491083

1050-
final String parameterTypeTernName = this.properties.getProperty(methodPrefix + ".arg" + idx + ".typeTernName");
1084+
String parameterTypeTernName = this.properties.getProperty(methodPrefix + "." + parameterName + ".typeTernName");
1085+
if ((parameterTypeTernName == null || parameterTypeTernName.isEmpty()) && !parameterName.equals("arg" + idx))
1086+
{
1087+
parameterTypeTernName = this.properties.getProperty(methodPrefix + ".arg" + idx + ".typeTernName");
1088+
}
1089+
10511090
if (parameterTypeTernName != null && !parameterTypeTernName.isEmpty())
10521091
{
10531092
typeName = parameterTypeTernName;

repository/src/main/resources/alfresco/module/ootbee-support-tools-repo/jsconsole-tern.properties

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ type.org.alfresco.service.ServiceRegistry.nameOnly=true
6464
type.org.activiti.engine.delegate.VariableScope.nameOnly=true
6565

6666
type.org.alfresco.repo.jscript.ScriptNode.properties.typeTernName=NodeProperties
67-
type.org.alfresco.repo.jscript.Person.getImmutableProperties.in.String.out.ScriptableHashMap.typeTernName=NodeProperties
67+
type.org.alfresco.repo.jscript.Person.getImmutableProperties.out.ScriptableHashMap.in.String.typeTernName=NodeProperties
6868
type.org.alfresco.repo.workflow.jscript.JscriptWorkflowTask.properties.typeTernName=TaskProperties
69-
type.org.alfresco.repo.jscript.Search.queryResultSet.in.Object.out.Scriptable.typeTernName=SearchResultSetMeta
69+
type.org.alfresco.repo.jscript.Search.queryResultSet.out.Scriptable.in.Object.typeTernName=SearchResultSetMeta
7070

7171
# native char is handled as a number
7272
type.java.lang.String.charAt.out.char.in.int.returnTypeTernName=number
@@ -90,38 +90,38 @@ type.org.alfresco.enterprise.repo.management.script.ScriptMBean.setJmxValueConve
9090
# some methods / properties use a generic Scriptable type for representing native arrays
9191
type.org.alfresco.repo.jscript.ScriptNode.children.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
9292
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
93-
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.in.boolean.boolean.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
94-
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.in.boolean.boolean.Object.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
95-
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.in.boolean.boolean.Object.int.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
96-
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.in.boolean.boolean.Object.int.int.int.String.Boolean.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
97-
type.org.alfresco.repo.jscript.ScriptNode.childrenByXPath.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
98-
type.org.alfresco.repo.jscript.ScriptNode.getChildAssocsByType.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
99-
type.org.alfresco.repo.jscript.ScriptNode.getPropertyNames.in.boolean.out.Scriptable.typeClassName=[Ljava.lang.String;
93+
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.in.boolean.boolean.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
94+
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.in.boolean.boolean.Object.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
95+
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.in.boolean.boolean.Object.int.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
96+
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.in.boolean.boolean.Object.int.int.int.String.Boolean.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
97+
type.org.alfresco.repo.jscript.ScriptNode.childrenByXPath.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
98+
type.org.alfresco.repo.jscript.ScriptNode.getChildAssocsByType.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
99+
type.org.alfresco.repo.jscript.ScriptNode.getPropertyNames.out.Scriptable.in.boolean.typeClassName=[Ljava.lang.String;
100100
type.org.alfresco.repo.jscript.ScriptNode.typePropertyNames.typeClassName=[Ljava.lang.String;
101-
type.org.alfresco.repo.jscript.ScriptNode.getTypePropertyNames.in.boolean.out.Scriptable.typeClassName=[Ljava.lang.String;
102-
type.org.alfresco.repo.jscript.Classification.getAllCategoryNodes.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.CategoryNode;
103-
type.org.alfresco.repo.jscript.Classification.getCategoryUsage.in.String.int.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.Classification$Tag;
104-
type.org.alfresco.repo.jscript.Classification.getRootCategories.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.CategoryNode;
105-
type.org.alfresco.repo.jscript.Search.luceneSearch.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
106-
type.org.alfresco.repo.jscript.Search.luceneSearch.in.String.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
107-
type.org.alfresco.repo.jscript.Search.luceneSearch.in.String.String.boolean.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
108-
type.org.alfresco.repo.jscript.Search.luceneSearch.in.String.String.boolean.int.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
109-
type.org.alfresco.repo.jscript.Search.luceneSearch.in.String.String.String.boolean.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
110-
type.org.alfresco.repo.jscript.Search.luceneSearch.in.String.String.String.boolean.int.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
111-
type.org.alfresco.repo.jscript.Search.query.in.Object.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
112-
type.org.alfresco.repo.jscript.Search.savedSearch.in.ScriptNode.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
113-
type.org.alfresco.repo.jscript.Search.savedSearch.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
114-
type.org.alfresco.repo.jscript.Search.selectNodes.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
115-
type.org.alfresco.repo.jscript.Search.selectNodes.in.String.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
116-
type.org.alfresco.repo.jscript.Search.xpathSearch.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
117-
type.org.alfresco.repo.jscript.Search.xpathSearch.in.String.String.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
118-
type.org.alfresco.repo.jscript.People.getContainerGroups.in.ScriptNode.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
119-
type.org.alfresco.repo.jscript.People.getMembers.in.ScriptNode.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
120-
type.org.alfresco.repo.jscript.People.getMembers.in.ScriptNode.boolean.out.Scriptable.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
101+
type.org.alfresco.repo.jscript.ScriptNode.getTypePropertyNames.out.Scriptable.in.boolean.typeClassName=[Ljava.lang.String;
102+
type.org.alfresco.repo.jscript.Classification.getAllCategoryNodes.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.CategoryNode;
103+
type.org.alfresco.repo.jscript.Classification.getCategoryUsage.out.Scriptable.in.String.int.typeClassName=[Lorg.alfresco.repo.jscript.Classification$Tag;
104+
type.org.alfresco.repo.jscript.Classification.getRootCategories.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.CategoryNode;
105+
type.org.alfresco.repo.jscript.Search.luceneSearch.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
106+
type.org.alfresco.repo.jscript.Search.luceneSearch.out.Scriptable.in.String.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
107+
type.org.alfresco.repo.jscript.Search.luceneSearch.out.Scriptable.in.String.String.boolean.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
108+
type.org.alfresco.repo.jscript.Search.luceneSearch.out.Scriptable.in.String.String.boolean.int.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
109+
type.org.alfresco.repo.jscript.Search.luceneSearch.out.Scriptable.in.String.String.String.boolean.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
110+
type.org.alfresco.repo.jscript.Search.luceneSearch.out.Scriptable.in.String.String.String.boolean.int.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
111+
type.org.alfresco.repo.jscript.Search.query.out.Scriptable.in.Object.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
112+
type.org.alfresco.repo.jscript.Search.savedSearch.out.Scriptable.in.ScriptNode.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
113+
type.org.alfresco.repo.jscript.Search.savedSearch.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
114+
type.org.alfresco.repo.jscript.Search.selectNodes.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
115+
type.org.alfresco.repo.jscript.Search.selectNodes.out.Scriptable.in.String.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
116+
type.org.alfresco.repo.jscript.Search.xpathSearch.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
117+
type.org.alfresco.repo.jscript.Search.xpathSearch.out.Scriptable.in.String.String.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
118+
type.org.alfresco.repo.jscript.People.getContainerGroups.out.Scriptable.in.ScriptNode.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
119+
type.org.alfresco.repo.jscript.People.getMembers.out.Scriptable.in.ScriptNode.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
120+
type.org.alfresco.repo.jscript.People.getMembers.out.Scriptable.in.ScriptNode.boolean.typeClassName=[Lorg.alfresco.repo.jscript.ScriptNode;
121121
# don't know why, but getPeople returns array of NodeRef instead of ScriptNode
122-
type.org.alfresco.repo.jscript.People.getPeople.in.String.out.Scriptable.typeClassName=[Lorg.alfresco.service.cmr.repository.NodeRef;
123-
type.org.alfresco.repo.jscript.People.getPeople.in.String.int.out.Scriptable.typeClassName=[Lorg.alfresco.service.cmr.repository.NodeRef;
124-
type.org.alfresco.repo.jscript.People.getPeople.in.String.int.String.boolean.out.Scriptable.typeClassName=[Lorg.alfresco.service.cmr.repository.NodeRef;
122+
type.org.alfresco.repo.jscript.People.getPeople.out.Scriptable.in.String.typeClassName=[Lorg.alfresco.service.cmr.repository.NodeRef;
123+
type.org.alfresco.repo.jscript.People.getPeople.out.Scriptable.in.String.int.typeClassName=[Lorg.alfresco.service.cmr.repository.NodeRef;
124+
type.org.alfresco.repo.jscript.People.getPeople.out.Scriptable.in.String.int.String.boolean.typeClassName=[Lorg.alfresco.service.cmr.repository.NodeRef;
125125
type.org.alfresco.repo.jscript.People.getPeoplePaging.in.String.ScriptPagingDetails.String.Boolean.out.Scriptable.typeClassName=[Lorg.alfresco.service.cmr.repository.NodeRef;
126126
type.org.alfresco.repo.workflow.jscript.WorkflowManager.assignedTasks.typeClassName=[Lorg.alfresco.repo.workflow.jscript.JscriptWorkflowTask;
127127
type.org.alfresco.repo.workflow.jscript.WorkflowManager.completedTasks.typeClassName=[Lorg.alfresco.repo.workflow.jscript.JscriptWorkflowTask;
@@ -134,14 +134,23 @@ type.org.alfresco.repo.workflow.jscript.JscriptWorkflowPath.tasks.typeClassName=
134134

135135
# some methods expect native-like objects
136136
# childFileFolders supports JavaString, native String and native array of native String - we opt for the latter for documentation
137-
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.in.boolean.boolean.Object.out.Scriptable.arg2.typeTernName=[string]
138-
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.in.boolean.boolean.Object.int.out.Scriptable.arg2.typeTernName=[string]
139-
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.in.boolean.boolean.Object.int.int.int.String.Boolean.String.out.Scriptable.arg2.typeTernName=[string]
137+
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.in.boolean.boolean.Object.arg2.typeTernName=[string]
138+
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.in.boolean.boolean.Object.int.arg2.typeTernName=[string]
139+
type.org.alfresco.repo.jscript.ScriptNode.childFileFolders.out.Scriptable.in.boolean.boolean.Object.int.int.int.String.Boolean.String.arg2.typeTernName=[string]
140+
# createNode supports a native Object-like structure for properties
141+
type.org.alfresco.repo.jscript.ScriptNode.createNode.out.ScriptNode.in.String.String.Object.arg2.typeTernName=object
142+
type.org.alfresco.repo.jscript.ScriptNode.createNode.out.ScriptNode.in.String.String.Object.String.arg2.typeTernName=object
143+
type.org.alfresco.repo.jscript.ScriptNode.createNode.out.ScriptNode.in.String.String.Object.String.String.String.arg2.typeTernName=object
144+
# addAspect supports a native Object-like structure for properties
145+
type.org.alfresco.repo.jscript.ScriptNode.addAspect.out.boolean.in.String.Object.arg1.typeTernName=object
146+
# processTemplate supports a native Object-like structure for arguments
147+
type.org.alfresco.repo.jscript.ScriptNode.processTemplate.out.String.in.ScriptNode.Object.arg1.typeTernName=object
148+
type.org.alfresco.repo.jscript.ScriptNode.processTemplate.out.String.in.String.Object.arg1.typeTernName=object
140149

141150
type.org.springframework.extensions.webscripts.ConfigModel.script.typeClassName=java.lang.String
142151

143-
type.org.springframework.extensions.webscripts.ScriptMessage.get.in.String.Scriptable.out.String.arg1.typeClassName=[Ljava.lang.String;
144-
type.org.springframework.extensions.webscripts.ScriptMessage.get.in.String.Scriptable.out.String.arg1.typeTernName=[string]
152+
type.org.springframework.extensions.webscripts.ScriptMessage.get.out.String.in.String.Scriptable.arg1.typeClassName=[Ljava.lang.String;
153+
type.org.springframework.extensions.webscripts.ScriptMessage.get.out.String.in.String.Scriptable.arg1.typeTernName=[string]
145154

146155
# default doc without I18n
147156
type.java.lang.String.ternDoc=Java representation of a string value not wrapped into a native Rhino / JS string

0 commit comments

Comments
 (0)