Skip to content

Commit

Permalink
Fixing JSON Bug with duplication and autoformat (#181)
Browse files Browse the repository at this point in the history
* Fixing JSON plugin

* Moving changes

* Removing local file

---------

Co-authored-by: NFALKEN <[email protected]>
  • Loading branch information
nojomyth-dev and NFALKEN authored Jul 3, 2024
1 parent e8f9806 commit 5248904
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 95 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ openjdk-14*
share/
zulu13*
wix
hs_err*
hs_err*

local-repo.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.correomqtt.gui.utils;

import java.util.Arrays;
import javafx.beans.value.ChangeListener;
import org.correomqtt.core.plugin.PluginManager;
import org.correomqtt.di.DefaultBean;
Expand Down Expand Up @@ -60,7 +61,7 @@ public Format autoFormatPayload(final String payload, boolean doFormatting, Stri
.orElseThrow(() -> new IllegalArgumentException("Plain format did not match."));


//ChangeListener<String> listener is needed to disable it when the text of the PublishCodeArea changes. It is reenabled after the manipulation.
// ChangeListener<String> listener is needed to disable it when the text of the PublishCodeArea changes. It is reenabled after the manipulation.
if (listener != null) {
codeArea.textProperty().removeListener(listener);
}
Expand All @@ -71,10 +72,6 @@ public Format autoFormatPayload(final String payload, boolean doFormatting, Stri
codeArea.setStyleSpans(0, foundFormat.getFxSpans());
} catch (Exception e) {
LOGGER.error("Formatter failed. ", e);
foundFormat = new Plain();
foundFormat.setText(payload);
codeArea.replaceText(0, 0, foundFormat.getPrettyString());
codeArea.setStyleSpans(0, foundFormat.getFxSpans());
}

if (listener != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package org.correomqtt.plugin.json_format;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.PrettyPrinter;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
import org.correomqtt.gui.plugin.spi.DetailViewFormatHook;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;
Expand All @@ -13,53 +21,19 @@
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Extension
public class JsonFormat implements DetailViewFormatHook {

private static final Logger LOGGER = LoggerFactory.getLogger(JsonFormat.class);

private static final Pattern JSON_PATTERN = Pattern.compile(
"(?<OBJECT>(?<KEYOBJECT>\"(\\\\.|[^\"])*\")\\h*([:])\\h*([{]))" +
"|(?<ARRAY>(?<KEYARRAY>\"(\\\\.|[^\"])*\")\\h*([:])\\h*([\\[]))" +
"|(?<NUMBER>(?<KEYNUMBER>\"(\\\\.|[^\"])*\")\\h*([:])\\h*(?<VALUENUMBER>[0-9]+))" +
"|(?<NULL>(?<KEYNULL>\"(\\\\.|[^\"])*\")\\h*([:])\\h*(?<VALUENULL>null))" +
"|(?<STRING>(?<KEYSTRING>\"(\\\\.|[^\"])*\")\\h*([:])\\h*(?<VALUESTRING>\"(\\\\.|[^\"])*\"))" +
"|(?<BOOLEAN>(?<KEYBOOLEAN>\"(\\\\.|[^\"])*\")\\h*([:])\\h*(?<VALUEBOOLEAN>true|false))" +
"|(?<SINGLESTRING>\"(\\\\.|[^\"])*\")" +
"|(?<SINGLENUMBER>[0-9]+)" +
"|(?<SINGLENULL>null)" +
"|(?<SINGLEBOOLEAN>true|false)"
);

private static final String OBJECT_GROUP = "OBJECT";
private static final String ARRAY_GROUP = "ARRAY";
private static final String NUMBER_GROUP = "NUMBER";
private static final String NULL_GROUP = "NULL";
private static final String STRING_GROUP = "STRING";
private static final String BOOLEAN_GROUP = "BOOLEAN";
private static final String KEYNULL_GROUP = "KEYNULL";
private static final String VALUENULL_GROUP = "VALUENULL";
private static final String KEYBOOLEAN_GROUP = "KEYBOOLEAN";
private static final String VALUEBOOLEAN_GROUP = "VALUEBOOLEAN";
private static final String KEYSTRING_GROUP = "KEYSTRING";
private static final String VALUESTRING_GROUP = "VALUESTRING";
private static final String KEYNUMBER_GROUP = "KEYNUMBER";
private static final String VALUENUMBER_GROUP = "VALUENUMBER";
private static final String KEYOBJECT_GROUP = "KEYOBJECT";
private static final String KEYARRAY_GROUP = "KEYARRAY";
private static final String SINGLESTRING_GROUP = "SINGLESTRING";
private static final String SINGLENUMBER_GROUP = "SINGLENUMBER";
private static final String SINGLENULL_GROUP = "SINGLENULL";
private static final String SINGLEBOOLEAN_GROUP = "SINGLEBOOLEAN";

private static final String KEY_CLASS = "keyJSON";
private static final String STRING_CLASS = "valueJSON";
private static final String NUMBER_CLASS = "numberJSON";
private static final String BOOLEAN_CLASS = "booleanJSON";
private static final String NULL_CLASS = "nullJSON";
private static ObjectMapper OBJECT_MAPPER;
private static final PrettyPrinter PRETTY_PRINTER = new DefaultPrettyPrinter().withObjectIndenter(new DefaultIndenter().withLinefeed("\n"));

private String text;
private Object jsonObject;
Expand All @@ -76,91 +50,118 @@ public boolean isValid() {
}

private Object getParsedJsonObject() {

if (jsonObject == null) {
jsonObject = createJsonObject();
}

return jsonObject;
}

private static ObjectMapper getObjectMapper() {

if (OBJECT_MAPPER == null) {

OBJECT_MAPPER = new ObjectMapper();
OBJECT_MAPPER.configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
}

return OBJECT_MAPPER;
}

private Object createJsonObject() {

try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
jsonObject = objectMapper.readValue(text, Object.class);
jsonObject = getObjectMapper().readValue(text, Object.class);
} catch (IOException e) {
LOGGER.trace("JSON could not be parsed. ", e);
return null;
}

return jsonObject;
}

@Override
public String getPrettyString() {

if (getParsedJsonObject() == null) {
return text;
}

try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(getParsedJsonObject());
return getObjectMapper().writer(PRETTY_PRINTER).writeValueAsString(getParsedJsonObject());
} catch (JsonProcessingException e) {
LOGGER.trace("Could not write pretty JSON. ", e);
return text;
}
}

public static String mapJsonToStyle(JsonToken jsonToken) {

if (jsonToken == null) {
return "";
}

return switch (jsonToken) {
case FIELD_NAME -> KEY_CLASS;
case VALUE_STRING -> STRING_CLASS;
case VALUE_NUMBER_FLOAT, VALUE_NUMBER_INT -> NUMBER_CLASS;
case VALUE_NULL -> NULL_CLASS;
case VALUE_TRUE, VALUE_FALSE -> BOOLEAN_CLASS;
default -> "";
};
}

private List<JsonMatch> getMatches(String json) {

List<JsonMatch> matches = new ArrayList<>();

try (JsonParser parser = new JsonFactory().createParser(json)) {
while (!parser.isClosed()) {
var token = parser.nextToken();
int start = (int) parser.getTokenLocation().getCharOffset();
int end = start + parser.getTextLength();

// parser does not include " by default
if (token == JsonToken.VALUE_STRING || token == JsonToken.FIELD_NAME) {
end += 2;
}

String styleClass = mapJsonToStyle(token);

if (!styleClass.isEmpty()) {
JsonMatch match = new JsonMatch(styleClass, start, end);
matches.add(match);
}
}
} catch (IOException e) {
// if not valid json, just ignore
}

return matches;
}

@Override
public StyleSpans<Collection<String>> getFxSpans() {
String prettyString = getPrettyString();

StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
var prettyString = getPrettyString();
int lastPos = 0;

int lastKwEnd = 0;
Matcher matcher = JSON_PATTERN.matcher(prettyString);
while (matcher.find()) {
spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
if (matcher.group(OBJECT_GROUP) != null) {
spansBuilder.add(Collections.emptyList(), matcher.start(KEYOBJECT_GROUP) - matcher.start());
spansBuilder.add(Collections.singleton(KEY_CLASS), matcher.end(KEYOBJECT_GROUP) - matcher.start(KEYOBJECT_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.end() - matcher.end(KEYOBJECT_GROUP));
} else if (matcher.group(ARRAY_GROUP) != null) {
spansBuilder.add(Collections.emptyList(), matcher.start(KEYARRAY_GROUP) - matcher.start());
spansBuilder.add(Collections.singleton(KEY_CLASS), matcher.end(KEYARRAY_GROUP) - matcher.start(KEYARRAY_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.end() - matcher.end(KEYARRAY_GROUP));
} else if (matcher.group(NUMBER_GROUP) != null) {
spansBuilder.add(Collections.emptyList(), matcher.start(KEYNUMBER_GROUP) - matcher.start());
spansBuilder.add(Collections.singleton(KEY_CLASS), matcher.end(KEYNUMBER_GROUP) - matcher.start(KEYNUMBER_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.start(VALUENUMBER_GROUP) - matcher.end(KEYNUMBER_GROUP));
spansBuilder.add(Collections.singleton(NUMBER_CLASS), matcher.end(VALUENUMBER_GROUP) - matcher.start(VALUENUMBER_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.end() - matcher.end(VALUENUMBER_GROUP));
} else if (matcher.group(BOOLEAN_GROUP) != null) {
spansBuilder.add(Collections.emptyList(), matcher.start(KEYBOOLEAN_GROUP) - matcher.start());
spansBuilder.add(Collections.singleton(KEY_CLASS), matcher.end(KEYBOOLEAN_GROUP) - matcher.start(KEYBOOLEAN_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.start(VALUEBOOLEAN_GROUP) - matcher.end(KEYBOOLEAN_GROUP));
spansBuilder.add(Collections.singleton(BOOLEAN_CLASS), matcher.end(VALUEBOOLEAN_GROUP) - matcher.start(VALUEBOOLEAN_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.end() - matcher.end(VALUEBOOLEAN_GROUP));
} else if (matcher.group(NULL_GROUP) != null) {
spansBuilder.add(Collections.emptyList(), matcher.start(KEYNULL_GROUP) - matcher.start());
spansBuilder.add(Collections.singleton(KEY_CLASS), matcher.end(KEYNULL_GROUP) - matcher.start(KEYNULL_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.start(VALUENULL_GROUP) - matcher.end(KEYNULL_GROUP));
spansBuilder.add(Collections.singleton(NULL_CLASS), matcher.end(VALUENULL_GROUP) - matcher.start(VALUENULL_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.end() - matcher.end(VALUENULL_GROUP));
} else if (matcher.group(STRING_GROUP) != null) {
spansBuilder.add(Collections.emptyList(), matcher.start(KEYSTRING_GROUP) - matcher.start());
spansBuilder.add(Collections.singleton(KEY_CLASS), matcher.end(KEYSTRING_GROUP) - matcher.start(KEYSTRING_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.start(VALUESTRING_GROUP) - matcher.end(KEYSTRING_GROUP));
spansBuilder.add(Collections.singleton(STRING_CLASS), matcher.end(VALUESTRING_GROUP) - matcher.start(VALUESTRING_GROUP));
spansBuilder.add(Collections.emptyList(), matcher.end() - matcher.end(VALUESTRING_GROUP));
} else if (matcher.group(SINGLESTRING_GROUP) != null) {
spansBuilder.add(Collections.singleton(STRING_CLASS), matcher.end() - matcher.start());
} else if (matcher.group(SINGLENUMBER_GROUP) != null) {
spansBuilder.add(Collections.singleton(NUMBER_CLASS), matcher.end() - matcher.start());
} else if (matcher.group(SINGLEBOOLEAN_GROUP) != null) {
spansBuilder.add(Collections.singleton(BOOLEAN_CLASS), matcher.end() - matcher.start());
} else if (matcher.group(SINGLENULL_GROUP) != null) {
spansBuilder.add(Collections.singleton(NULL_CLASS), matcher.end() - matcher.start());
for (JsonMatch match : getMatches(prettyString)) {
if (match.getStart() > lastPos) {
int length = match.getStart() - lastPos;
spansBuilder.add(Collections.emptyList(), length);
}
lastKwEnd = matcher.end();

// add current token with style, then remember pos for next iteration
spansBuilder.add(Collections.singleton(match.getType()), match.getEnd() - match.getStart());
lastPos = match.getEnd();
}

// prevent exception if empty string
if (lastPos == 0) {
spansBuilder.add(Collections.emptyList(), prettyString.length());
}

return spansBuilder.create();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.correomqtt.plugin.json_format;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class JsonMatch {
String type;
int start;
int end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,12 @@ private static RepoPluginInfoDTO getRepoPluginInfoDto(String moduleId,
if (repoBaseUrl != null) {
url = repoBaseUrl + "/" + jarFileName;
} else {
url = "file://" + files[0].getAbsolutePath();
// Fixes plugins not being able to be loaded under windows
if(System.getProperty("os.name").contains("Windows")) {
url = "file:///" + files[0].getAbsolutePath();
} else {
url = "file://" + files[0].getAbsolutePath();
}
}
List<RepoPluginInfoDTO.PluginRelease> releaseList = oldPluginInfo == null ?
new ArrayList<>() :
Expand Down

0 comments on commit 5248904

Please sign in to comment.