Skip to content

Commit

Permalink
Support enums in Collections in Highlighting
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronweissler committed Mar 22, 2022
1 parent 4e1ede8 commit a4643e2
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,22 @@ let InspectitOcelotHighlightRules = function () {
regex: /:\s*/,
};

const mapKeysRules = [
{
token: ['variable', 'keyword', 'text'],
regex: /(['][^']*?[']\s*(?=:))(:)(\s*)/,
},
{
token: ['variable', 'keyword', 'text'],
regex: /([^\s]+?\s*(?=:))(:)(\s*)/,
},
];

const listRule = {
token: 'list.markup',
regex: `- `,
};

const invalidRule = {
token: 'invalid.illegal',
regex: /.*/,
Expand Down Expand Up @@ -267,15 +283,15 @@ let InspectitOcelotHighlightRules = function () {
if (key === KEY_START) {
current_state_name = key;
} else {
current_state_name = `${key}-${parent_name}`;
current_state_name = `${parent_name}-${key}`;
}

// Rules for highlighting are kept in a list per state, this list for the state corresponding to the
// current key is created here.

// The list already contains two rules that are needed for every key and are at the correct position if
// added now (the order of rules matters for evaluation, if a string matches a regex in a previous rule,
// later rules are not evaluated anymore.
// later rules are not evaluated anymore).
let rules_for_current_key = [commentRule, defaultRule, keywordRule, jsonStartRule];

// The info on what values should be behind a key, is within a nested map. This map is retrieved here.
Expand Down Expand Up @@ -327,7 +343,7 @@ let InspectitOcelotHighlightRules = function () {
// state. These rules are created here.

for (let attribute of attributes_map.keys()) {
let new_state_name = `${attribute}-${current_state_name}`;
let new_state_name = `${current_state_name}-${attribute}`;
let inner_map = new Map(Object.entries(attributes_map.get(attribute)));
let content_type = inner_map.get(KEY_TYPE);
current_rules.push({
Expand Down Expand Up @@ -398,70 +414,78 @@ let InspectitOcelotHighlightRules = function () {
function mapToRulesTypeMap(inner_map, rules_for_current_key, current_state_name, nested_level) {
// Depending on whether Maps contain more InspectitConfig-specific objects
// or not, e.g. Strings or int, as values, different rules need to be added.
let map_content_type = inner_map.get(KEY_MAP_CONTENT_TYPE);

if (map_content_type === VALUE_TYPE_OBJECT || map_content_type === VALUE_TYPE_YAML) {
// If the map contains InspectitConfig-specific objects or arbitrary YAML as values, a new state is needed that is entered
// after seeing any key for the map.
let sub_state_name = `single-${current_state_name}`;

// the rules for the new substate are created in mapToRulesMapSubkey
mapToRulesMapSubkey(inner_map, sub_state_name, map_content_type, nested_level);

// Any text is accepted as a key after which the new sub-state is entered for its values.
rules_for_current_key.push({
token: 'variable',
regex: /(.+?(?=:))/,
next: sub_state_name,
onMatch: function () {
addToStatesMap(sub_state_name, current_state_name, map_content_type, nested_level);
return this.token;
},
});
rules_for_current_key.push({
token: 'variable',
regex: /(['][^']*?['](?=:))/,
next: sub_state_name,
onMatch: function () {
addToStatesMap(sub_state_name, current_state_name, map_content_type, nested_level);
return this.token;
},
});

// only keys with arbitrary names are allowed in this state, so the invalidRule is added
rules_for_current_key.push(invalidRule);
} else if (map_content_type === VALUE_TYPE_TEXT) {
// if the map simply contains text content, no new sub-state is needed and instead, keys are simply
// highlighted as variables again and the textRules are added to highlight any text in the values properly.
rules_for_current_key.push(
{
token: ['variable', 'keyword'],
regex: /(['][^']*?[']\s*(?=:))(:)/,
},
{
token: ['variable', 'keyword'],
regex: /([^\s]+?\s*(?=:))(:)/,
}
);
rules_for_current_key = rules_for_current_key.concat(textRules);
const map_content_type = inner_map.get(KEY_MAP_CONTENT_TYPE);
switch (map_content_type) {
case VALUE_TYPE_OBJECT:
case VALUE_TYPE_YAML: {
// If the map contains InspectitConfig-specific objects or arbitrary YAML as values, a new state is needed that is entered
// after seeing any key for the map.
let sub_state_name = `${current_state_name}-single`;

// the rules for the new substate are created in mapToRulesMapSubkey
mapToRulesCollectionSubkey(inner_map, sub_state_name, map_content_type, nested_level, KEY_MAP_CONTENTS);

// Any text is accepted as a key after which the new sub-state is entered for its values.
rules_for_current_key.push({
token: 'variable',
regex: /(.+?(?=:))/,
next: sub_state_name,
onMatch: function () {
addToStatesMap(sub_state_name, current_state_name, map_content_type, nested_level);
return this.token;
},
});
rules_for_current_key.push({
token: 'variable',
regex: /(['][^']*?['](?=:))/,
next: sub_state_name,
onMatch: function () {
addToStatesMap(sub_state_name, current_state_name, map_content_type, nested_level);
return this.token;
},
});

// only new keys with arbitrary names are allowed in this state, so the invalidRule is added
rules_for_current_key.push(invalidRule);
break;
}
case VALUE_TYPE_ENUM: {
// if the map contains enum content, no new sub-state is needed and instead, keys are simply
// highlighted as variables and the rules for the enum are added to highlight the values properly.
rules_for_current_key = rules_for_current_key.concat(mapKeysRules);
let enum_values = inner_map.get(KEY_ENUM_VALUES);
rules_for_current_key = rulesForEnum(rules_for_current_key, current_state_name, enum_values);
break;
}
case VALUE_TYPE_TEXT: {
// if the map contains text content, there is also no new sub-state needed and instead, keys are simply
// highlighted as variables again and the textRules are added to highlight any text in the values properly.
rules_for_current_key = rules_for_current_key.concat(mapKeysRules);
rules_for_current_key = rules_for_current_key.concat(textRules);
break;
}
}

return rules_for_current_key;
}

// Generates the rules for the contents of a Map if it contains InspectitConfig-specific objects.
function mapToRulesMapSubkey(inner_map, sub_state_name, map_content_type, nested_level) {
function mapToRulesCollectionSubkey(inner_map, sub_state_name, content_type, nested_level, contents_key) {
// As before the list of rules for the state is created with the comment-rule already in it.
let rules_for_sub_state = [commentRule, defaultRule, keywordRule, jsonStartRule];

if (map_content_type === VALUE_TYPE_OBJECT) {
// If the map contains InspectitConfig-specific objects as values, these objects' attributes will be behind
// the key 'map-contents' in the inner_map.
let contents_map = new Map(Object.entries(inner_map.get(KEY_MAP_CONTENTS)));

rules_for_sub_state = rulesForObject(contents_map, rules_for_sub_state, sub_state_name, nested_level + 1);
} else if (map_content_type === VALUE_TYPE_YAML) {
rules_for_sub_state = rulesForYaml(rules_for_sub_state);
switch (content_type) {
case VALUE_TYPE_OBJECT: {
// If the map contains InspectitConfig-specific objects as values, these objects' attributes will be behind
// the key 'map-contents' in the inner_map.
let contents_map = new Map(Object.entries(inner_map.get(contents_key)));
rules_for_sub_state = rulesForObject(contents_map, rules_for_sub_state, sub_state_name, nested_level + 1);
break;
}
case VALUE_TYPE_YAML: {
rules_for_sub_state = rulesForYaml(rules_for_sub_state);
break;
}
}
rules_for_sub_state = indentRules.concat(rules_for_sub_state);
allRules.set(sub_state_name, rules_for_sub_state);
Expand All @@ -471,33 +495,39 @@ let InspectitOcelotHighlightRules = function () {
function mapToRulesTypeList(inner_map, rules_for_current_key, current_state_name, nested_level) {
// If the list contains InspectitConfig-specific objects as values, a new state is needed that is entered
// after seeing the start of the list.
let list_content_type = inner_map.get(KEY_LIST_CONTENT_TYPE);
if (list_content_type === VALUE_TYPE_OBJECT) {
let sub_state_name = `single-${current_state_name}`;
rules_for_current_key.push({
token: 'list.markup',
regex: /\s*[-?](?:$|\s)/,
next: sub_state_name,
onMatch: function () {
addToStatesMap(sub_state_name, current_state_name, list_content_type, nested_level);
return this.token;
},
});

let rules_for_sub_state = [];
let contents_map = new Map(Object.entries(inner_map.get(KEY_LIST_CONTENTS)));

rules_for_sub_state = rulesForObject(contents_map, rules_for_sub_state, sub_state_name, nested_level + 1);
rules_for_sub_state = indentRules.concat(rules_for_sub_state);
allRules.set(sub_state_name, rules_for_sub_state);
} else if (inner_map.get(KEY_LIST_CONTENT_TYPE) === VALUE_TYPE_TEXT) {
// if the map simply contains text content, no new sub-state is needed and instead simply a rule to highlight
// the list beginning correctly is needed and the textRules are added to highlight any text in the list
rules_for_current_key.push({
token: 'list.markup',
regex: `- `,
});
rules_for_current_key = rules_for_current_key.concat(textRules);
const list_content_type = inner_map.get(KEY_LIST_CONTENT_TYPE);
switch (list_content_type) {
case VALUE_TYPE_OBJECT:
case VALUE_TYPE_YAML: {
let sub_state_name = `${current_state_name}-single`;
rules_for_current_key.push({
token: 'list.markup',
regex: /\s*[-?](?:$|\s)/,
next: sub_state_name,
onMatch: function () {
addToStatesMap(sub_state_name, current_state_name, list_content_type, nested_level);
return this.token;
},
});
mapToRulesCollectionSubkey(inner_map, sub_state_name, list_content_type, nested_level, KEY_LIST_CONTENTS);
rules_for_current_key.push(invalidRule);
break;
}
case VALUE_TYPE_ENUM: {
// if the map contains text content, there is also new sub-state needed and instead a rule to highlight
// the list beginning correctly is added and the enum rules are added to highlight the values in the list
rules_for_current_key.push(listRule);
let enum_values = inner_map.get(KEY_ENUM_VALUES);
rules_for_current_key = rulesForEnum(rules_for_current_key, current_state_name, enum_values);
break;
}
case VALUE_TYPE_TEXT: {
// if the map contains text content, there is also new sub-state needed and instead a rule to highlight
// the list beginning correctly is added and the textRules are added to highlight the values in the list
rules_for_current_key.push(listRule);
rules_for_current_key = rules_for_current_key.concat(textRules);
break;
}
}

return rules_for_current_key;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -23,7 +22,6 @@
@RestController
public class HighlightRulesMapController extends AbstractBaseController {


@VisibleForTesting
static final String VALUE_TYPE_MAP = "map";

Expand Down Expand Up @@ -82,38 +80,33 @@ Map<String, Object> generateMap(Class<?> currentClass) {

innerMap.put(KEY_TYPE, VALUE_TYPE_MAP);
ParameterizedType type = (ParameterizedType) field.getGenericType();
Type valueType = type.getActualTypeArguments()[1];
Class<?> valueType = (Class<?>) type.getActualTypeArguments()[1];

generateMapCollections(innerMap, valueType, KEY_MAP_CONTENT_TYPE, KEY_MAP_CONTENTS);

} else if (field.getType().equals(java.util.List.class)) {

innerMap.put(KEY_TYPE, VALUE_TYPE_LIST);
ParameterizedType type = (ParameterizedType) field.getGenericType();
Type listContentType = type.getActualTypeArguments()[0];
Class<?> listContentType = (Class<?>) type.getActualTypeArguments()[0];

generateMapCollections(innerMap, listContentType, KEY_LIST_CONTENT_TYPE, KEY_LIST_CONTENTS);

} else if (field.getType().equals(java.lang.Object.class)) {

innerMap.put(KEY_TYPE, VALUE_TYPE_YAML);

} else if (field.getType().isEnum()){

List<String> enumValues = new ArrayList<>();
for (Field enumField : field.getType().getFields()) {
enumValues.add(enumField.getName());
}
} else if (field.getType().isEnum()) {
innerMap.put(KEY_TYPE, VALUE_TYPE_ENUM);
innerMap.put(KEY_ENUM_VALUES, enumValues);
innerMap.put(KEY_ENUM_VALUES, extractEnumValues(field.getType()));

} else if (field.getType().getName().startsWith("rocks.inspectit.ocelot.config.model")) {

innerMap.put(KEY_TYPE, VALUE_TYPE_OBJECT);
innerMap.put(KEY_OBJECT_ATTRIBUTES, generateMap(field.getType()));

} else if (currentClass.equals(GenericActionSettings.class) &&
(field.getName().equals("value") || field.getName().equals("valueBody"))) {
} else if (currentClass.equals(GenericActionSettings.class) && (field.getName()
.equals("value") || field.getName().equals("valueBody"))) {

innerMap.put(KEY_TYPE, VALUE_TYPE_JAVA);

Expand All @@ -128,21 +121,32 @@ Map<String, Object> generateMap(Class<?> currentClass) {
return currentClassMap;
}

private void generateMapCollections(Map<String, Object> innerMap, Type contentType, String keyContentType, String keyContents) {
private void generateMapCollections(Map<String, Object> innerMap, Class<?> contentType, String keyContentType, String keyContents) {
if (contentType.equals(Object.class)) {
innerMap.put(keyContentType, VALUE_TYPE_YAML);
} else if (contentType.isEnum()) {
innerMap.put(keyContentType, VALUE_TYPE_ENUM);
innerMap.put(KEY_ENUM_VALUES, extractEnumValues(contentType));
} else if (contentType.getTypeName().startsWith("rocks.inspectit.ocelot.config.model")) {
innerMap.put(keyContentType, VALUE_TYPE_OBJECT);
innerMap.put(keyContents, generateMap((Class<?>) contentType));
innerMap.put(keyContents, generateMap(contentType));
} else {
innerMap.put(keyContentType, VALUE_TYPE_TEXT);
}
}

private List<String> extractEnumValues(Class<?> currentEnum) {
List<String> enumValues = new ArrayList<>();
for (Field enumField : currentEnum.getFields()) {
enumValues.add(enumField.getName());
}
return enumValues;
}

@ApiOperation(value = "Get JSON for Highlight Rules Generation", notes = "")
@GetMapping(value = "highlight-rules", produces = MediaType.APPLICATION_JSON_VALUE)
public Map<String, Object> getHighlightRulesMap() {
return generateMap(InspectitConfig.class);
return generateMap(InspectitConfig.class);
}

}

0 comments on commit a4643e2

Please # to comment.