private JSONArray enrichProperties(String operatorClass, JSONArray properties) throws JSONException { JSONArray result = new JSONArray(); for (int i = 0; i < properties.length(); i++) { JSONObject propJ = properties.getJSONObject(i); String propName = WordUtils.capitalize(propJ.getString("name")); String getPrefix = (propJ.getString("type").equals("boolean") || propJ.getString("type").equals("java.lang.Boolean")) ? "is" : "get"; String setPrefix = "set"; OperatorClassInfo oci = getOperatorClassWithGetterSetter( operatorClass, setPrefix + propName, getPrefix + propName); if (oci == null) { result.put(propJ); continue; } MethodInfo setterInfo = oci.setMethods.get(setPrefix + propName); MethodInfo getterInfo = oci.getMethods.get(getPrefix + propName); if ((getterInfo != null && getterInfo.omitFromUI) || (setterInfo != null && setterInfo.omitFromUI)) { continue; } if (setterInfo != null) { addTagsToProperties(setterInfo, propJ); } else if (getterInfo != null) { addTagsToProperties(getterInfo, propJ); } result.put(propJ); } return result; }
private void addTagsToProperties(MethodInfo mi, JSONObject propJ) throws JSONException { // create description object. description tag enables the visual tools to display description of // keys/values // of a map property, items of a list, properties within a complex type. JSONObject descriptionObj = new JSONObject(); if (mi.comment != null) { descriptionObj.put("$", mi.comment); } for (Map.Entry<String, String> descEntry : mi.descriptions.entrySet()) { descriptionObj.put(descEntry.getKey(), descEntry.getValue()); } if (descriptionObj.length() > 0) { propJ.put("descriptions", descriptionObj); } // create useSchema object. useSchema tag is added to enable visual tools to be able to render a // text field // as a dropdown with choices populated from the schema attached to the port. JSONObject useSchemaObj = new JSONObject(); for (Map.Entry<String, String> useSchemaEntry : mi.useSchemas.entrySet()) { useSchemaObj.put(useSchemaEntry.getKey(), useSchemaEntry.getValue()); } if (useSchemaObj.length() > 0) { propJ.put("useSchema", useSchemaObj); } }
public JSONObject describeClass(Class<?> clazz) throws Exception { JSONObject desc = new JSONObject(); desc.put("name", clazz.getName()); if (clazz.isEnum()) { @SuppressWarnings("unchecked") Class<Enum<?>> enumClass = (Class<Enum<?>>) clazz; ArrayList<String> enumNames = Lists.newArrayList(); for (Enum<?> e : enumClass.getEnumConstants()) { enumNames.add(e.name()); } desc.put("enum", enumNames); } UI_TYPE ui_type = UI_TYPE.getEnumFor(clazz); if (ui_type != null) { desc.put("uiType", ui_type.getName()); } desc.put("properties", getClassProperties(clazz, 0)); return desc; }
private JSONObject setFieldAttributes(String clazz, CompactFieldNode field) throws JSONException { JSONObject port = new JSONObject(); port.put("name", field.getName()); TypeGraphVertex tgv = typeGraph.getTypeGraphVertex(clazz); putFieldDescription(field, port, tgv); List<CompactAnnotationNode> annotations = field.getVisibleAnnotations(); CompactAnnotationNode firstAnnotation; if (annotations != null && !annotations.isEmpty() && (firstAnnotation = field.getVisibleAnnotations().get(0)) != null) { for (Map.Entry<String, Object> entry : firstAnnotation.getAnnotations().entrySet()) { port.put(entry.getKey(), entry.getValue()); } } return port; }
@SuppressWarnings("unchecked") public void addDefaultValue(String className, JSONObject oper) throws Exception { ObjectMapper defaultValueMapper = ObjectMapperFactory.getOperatorValueSerializer(); Class<? extends Operator> clazz = (Class<? extends Operator>) classLoader.loadClass(className); if (clazz != null) { Operator operIns = clazz.newInstance(); String s = defaultValueMapper.writeValueAsString(operIns); oper.put("defaultValue", new JSONObject(s).get(className)); } }
private void putFieldDescription(CompactFieldNode field, JSONObject port, TypeGraphVertex tgv) throws JSONException { OperatorClassInfo oci = classInfo.get(tgv.typeName); if (oci != null) { String fieldDesc = oci.fields.get(field.getName()); if (fieldDesc != null) { port.put("description", fieldDesc); return; } } for (TypeGraphVertex ancestor : tgv.getAncestors()) { putFieldDescription(field, port, ancestor); } }
/** * Enrich portClassHier with class/interface names that map to a list of parent * classes/interfaces. For any class encountered, find its parents too.<br> * Also find the port types which have assignable schema classes. * * @param oper Operator to work on * @param portClassHierarchy In-Out param that contains a mapping of class/interface to its * parents * @param portTypesWithSchemaClasses Json that will contain all the ports which have any schema * classes. */ public void buildAdditionalPortInfo( JSONObject oper, JSONObject portClassHierarchy, JSONObject portTypesWithSchemaClasses) { try { JSONArray ports = oper.getJSONArray(OperatorDiscoverer.PORT_TYPE_INFO_KEY); for (int i = 0; i < ports.length(); i++) { JSONObject port = ports.getJSONObject(i); String portType = port.optString("type"); if (portType == null) { // skipping if port type is null continue; } if (typeGraph.size() == 0) { buildTypeGraph(); } try { // building port class hierarchy LinkedList<String> queue = Lists.newLinkedList(); queue.add(portType); while (!queue.isEmpty()) { String currentType = queue.remove(); if (portClassHierarchy.has(currentType)) { // already present in the json so we skip. continue; } List<String> immediateParents = typeGraph.getParents(currentType); if (immediateParents == null) { portClassHierarchy.put(currentType, Lists.<String>newArrayList()); continue; } portClassHierarchy.put(currentType, immediateParents); queue.addAll(immediateParents); } } catch (JSONException e) { LOG.warn("building port type hierarchy {}", portType, e); } // finding port types with schema classes if (portTypesWithSchemaClasses.has(portType)) { // already present in the json so skipping continue; } if (portType.equals("byte") || portType.equals("short") || portType.equals("char") || portType.equals("int") || portType.equals("long") || portType.equals("float") || portType.equals("double") || portType.equals("java.lang.String") || portType.equals("java.lang.Object")) { // ignoring primitives, strings and object types as this information is needed only for // complex types. continue; } if (port.has("typeArgs")) { // ignoring any type with generics continue; } boolean hasSchemaClasses = false; List<String> instantiableDescendants = typeGraph.getInstantiableDescendants(portType); if (instantiableDescendants != null) { for (String descendant : instantiableDescendants) { try { if (typeGraph.isInstantiableBean(descendant)) { hasSchemaClasses = true; break; } } catch (JSONException ex) { LOG.warn("checking descendant is instantiable {}", descendant); } } } portTypesWithSchemaClasses.put(portType, hasSchemaClasses); } } catch (JSONException e) { // should not reach this LOG.error("JSON Exception {}", e); throw new RuntimeException(e); } }
private JSONArray getClassProperties(Class<?> clazz, int level) throws IntrospectionException { JSONArray arr = new JSONArray(); TypeDiscoverer td = new TypeDiscoverer(); try { for (PropertyDescriptor pd : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) { Method readMethod = pd.getReadMethod(); if (readMethod != null) { if (readMethod.getDeclaringClass() == java.lang.Enum.class) { // skip getDeclaringClass continue; } else if ("class".equals(pd.getName())) { // skip getClass continue; } } else { // yields com.datatorrent.api.Context on JDK6 and // com.datatorrent.api.Context.OperatorContext with JDK7 if ("up".equals(pd.getName()) && com.datatorrent.api.Context.class.isAssignableFrom(pd.getPropertyType())) { continue; } } // LOG.info("name: " + pd.getName() + " type: " + pd.getPropertyType()); Class<?> propertyType = pd.getPropertyType(); if (propertyType != null) { JSONObject propertyObj = new JSONObject(); propertyObj.put("name", pd.getName()); propertyObj.put("canGet", readMethod != null); propertyObj.put("canSet", pd.getWriteMethod() != null); if (readMethod != null) { for (Class<?> c = clazz; c != null; c = c.getSuperclass()) { OperatorClassInfo oci = classInfo.get(c.getName()); if (oci != null) { MethodInfo getMethodInfo = oci.getMethods.get(readMethod.getName()); if (getMethodInfo != null) { addTagsToProperties(getMethodInfo, propertyObj); break; } } } // type can be a type symbol or parameterized type td.setTypeArguments(clazz, readMethod.getGenericReturnType(), propertyObj); } else { if (pd.getWriteMethod() != null) { td.setTypeArguments( clazz, pd.getWriteMethod().getGenericParameterTypes()[0], propertyObj); } } // if (!propertyType.isPrimitive() && !propertyType.isEnum() && !propertyType.isArray() && // !propertyType.getName().startsWith("java.lang") && level < MAX_PROPERTY_LEVELS) { // propertyObj.put("properties", getClassProperties(propertyType, level + 1)); // } arr.put(propertyObj); } } } catch (JSONException ex) { throw new RuntimeException(ex); } return arr; }
public JSONObject describeOperator(String clazz) throws Exception { TypeGraphVertex tgv = typeGraph.getTypeGraphVertex(clazz); if (tgv.isInstantiable()) { JSONObject response = new JSONObject(); JSONArray inputPorts = new JSONArray(); JSONArray outputPorts = new JSONArray(); // Get properties from ASM JSONObject operatorDescriptor = describeClassByASM(clazz); JSONArray properties = operatorDescriptor.getJSONArray("properties"); properties = enrichProperties(clazz, properties); JSONArray portTypeInfo = operatorDescriptor.getJSONArray("portTypeInfo"); List<CompactFieldNode> inputPortfields = typeGraph.getAllInputPorts(clazz); List<CompactFieldNode> outputPortfields = typeGraph.getAllOutputPorts(clazz); try { for (CompactFieldNode field : inputPortfields) { JSONObject inputPort = setFieldAttributes(clazz, field); if (!inputPort.has("optional")) { inputPort.put( "optional", false); // input port that is not annotated is default to be not optional } if (!inputPort.has(SCHEMA_REQUIRED_KEY)) { inputPort.put(SCHEMA_REQUIRED_KEY, false); } inputPorts.put(inputPort); } for (CompactFieldNode field : outputPortfields) { JSONObject outputPort = setFieldAttributes(clazz, field); if (!outputPort.has("optional")) { outputPort.put( "optional", true); // output port that is not annotated is default to be optional } if (!outputPort.has("error")) { outputPort.put("error", false); } if (!outputPort.has(SCHEMA_REQUIRED_KEY)) { outputPort.put(SCHEMA_REQUIRED_KEY, false); } outputPorts.put(outputPort); } response.put("name", clazz); response.put("properties", properties); response.put(PORT_TYPE_INFO_KEY, portTypeInfo); response.put("inputPorts", inputPorts); response.put("outputPorts", outputPorts); OperatorClassInfo oci = classInfo.get(clazz); if (oci != null) { if (oci.comment != null) { String[] descriptions; // first look for a <p> tag String keptPrefix = "<p>"; descriptions = oci.comment.split("<p>", 2); if (descriptions.length == 0) { keptPrefix = ""; // if no <p> tag, then look for a blank line descriptions = oci.comment.split("\n\n", 2); } if (descriptions.length > 0) { response.put("shortDesc", descriptions[0]); } if (descriptions.length > 1) { response.put("longDesc", keptPrefix + descriptions[1]); } } response.put("category", oci.tags.get("@category")); String displayName = oci.tags.get("@displayName"); if (displayName == null) { displayName = decamelizeClassName(ClassUtils.getShortClassName(clazz)); } response.put("displayName", displayName); String tags = oci.tags.get("@tags"); if (tags != null) { JSONArray tagArray = new JSONArray(); for (String tag : StringUtils.split(tags, ',')) { tagArray.put(tag.trim().toLowerCase()); } response.put("tags", tagArray); } String doclink = oci.tags.get("@doclink"); if (doclink != null) { response.put("doclink", doclink + "?" + getDocName(clazz)); } else if (clazz.startsWith("com.datatorrent.lib.") || clazz.startsWith("com.datatorrent.contrib.")) { response.put("doclink", DT_OPERATOR_DOCLINK_PREFIX + "?" + getDocName(clazz)); } } } catch (JSONException ex) { throw new RuntimeException(ex); } return response; } else { throw new UnsupportedOperationException(); } }