How to know what text has been deleted from a JTextPane - swing

I have added a document listener to a JTextPane. I want to know what text has been added or removed so I can take action if certain key words are entered. The insert part works just fine, but I do not know how to detect what text was deleted.
The insert works because the text is there and I can select it, but the delete has already removed the text so I get bad location exceptions sometimes.
I want to make reserved words that are not inside quotes bold so I need to know what has been removed, removing even one character (like a quote) could have a huge impact.
My code follows:
#Override
public void insertUpdate(DocumentEvent e)
{
Document doc = e.getDocument();
String i = "";
try
{
i = doc.getText(e.getOffset(), e.getLength());
}
catch(BadLocationException e1)
{
e1.printStackTrace();
}
System.out.println("INSERT:" + e + ":" + i);
}
#Override
public void removeUpdate(DocumentEvent e)
{
Document doc = e.getDocument();
String i = "";
try
{
i = doc.getText(e.getOffset(), e.getLength());
}
catch(BadLocationException e1)
{
e1.printStackTrace();
}
System.out.println("REMOVE:" + e + ":" + i);
}

This is strange that there is no simple way to get this information.
I've looked at the source code of Swing libraries for this. Of course - there is this information in DocumentEvent, which is of class AbstractDocument$DefaultDocumentEvent, which contains protected Vector<UndoableEdit> edits, which contains one element of type GapContent$RemoveUndo, which contains protected String string that is used only in this class (no other "package" classes get this) and this RemoveUndo class have no getter for this field.
Even toString didn't show it (because RemoveUndo hasn't overrided toString method):
[javax.swing.text.GapContent$RemoveUndo#6303ddfd hasBeenDone: true alive: true]
This is so strange for me that I belive that there is some other easy way to get the removed string and that I just don't know how to accomplish it.
One thing you can do is the most obvious:
final JTextArea textArea = new JTextArea();
textArea.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
previousText = textArea.getText();
}
});
textArea.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void removeUpdate(DocumentEvent e) {
if(previousText != null) {
String removedStr = previousText.substring(e.getOffset(), e.getOffset() + e.getLength());
System.out.println(removedStr);
}
}
#Override
public void insertUpdate(DocumentEvent e) {
}
#Override
public void changedUpdate(DocumentEvent e) {
}
});
where previousText is an instance variable.
or (the most nasty ever):
textArea.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void removeUpdate(DocumentEvent e) {
String removedString = getRemovedString(e);
System.out.println(removedString);
}
#Override
public void insertUpdate(DocumentEvent e) {
}
#Override
public void changedUpdate(DocumentEvent e) {
}
});
plus this method:
public static String getRemovedString(DocumentEvent e) {
try {
Field editsField = null;
Field[] fields = CompoundEdit.class.getDeclaredFields();
for(Field f : fields) {
if(f.getName().equals("edits")) {
editsField = f;
break;
}
}
editsField.setAccessible(true);
List edits = (List) editsField.get(e);
if(edits.size() != 1) {
return null;
}
Class<?> removeUndo = null;
for(Class<?> c : GapContent.class.getDeclaredClasses()) {
if(c.getSimpleName().equals("RemoveUndo")) {
removeUndo = c;
break;
}
}
Object removeUndoInstance = edits.get(0);
fields = removeUndo.getDeclaredFields();
Field stringField = null;
for(Field f : fields) {
if(f.getName().equals("string")) {
stringField = f;
break;
}
}
stringField.setAccessible(true);
return (String) stringField.get(removeUndoInstance);
}
catch(SecurityException e1) {
e1.printStackTrace();
}
catch(IllegalArgumentException e1) {
e1.printStackTrace();
}
catch(IllegalAccessException e1) {
e1.printStackTrace();
}
return null;
}

I had the same problem than you. And what Xeon explained help me a lot too. But after, i found a way to do that. In my case, i created a custom StyledDocument class that extends DefaultStyledDocument:
public class CustomStyledDocument extends DefaultStyledDocument
{
public CustomStyledDocument () {
super();
}
#Override
public void insertString(int offset, String string, AttributeSet as) throws BadLocationException {
super.insertString(offset, string, as);
}
#Override
public void remove(int offset, int i1) throws BadLocationException {
String previousText = getText(offset, i1);
super.remove(offset, i1);
}
}
So if you call getText method before you call super.remove(...), you will get the previous text.

Related

Keep the original Html format when scrapping a website element using Jsoup

The below code only retrieves the simple text but I want to retain the HTML format. Here is my sample code:
public class doIT extends AsyncTask<Void,Void,Void> {
//String words = wordToTranslate;
String translated;
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
try
{
Document document = Jsoup.connect("https://studentdevos.com/").get();
org.jsoup.select.Elements elements = document.getElementsByClass("post-entry");
translated = Jsoup.parse(elements.html()).wholeText();
}
catch (IOException e)
{
e.printStackTrace();
} return null;
}
#Override
protected void onPostExecute(Void aVoid)
{
tv_Jsoupe.setText(Html.fromHtml(translated));
super.onPostExecute(aVoid);
}
}
I think that the Jsoup.parse() is what retrieves the simple text.
code :
translated = elements.html();
Change this line as well from:
tv_Jsoupe.setText(Html.fromHtml(translated));
to
tv_Jsoupe.setText(translated);

How can I mock RabbitMQClient of io.quarkiverse.rabbitmqclient.RabbitMQClient and write junit for basic send and consume operation?

I'm new to the quarkus framework where I'm writing rabbitmq-client library based on quarkur framework. I'm using io.quarkiverse.rabbitmqclient.RabbitMQClient.
I need to write JUnit for basic send and consume operations, please help me with how can I write junit and mock RabbitMQClient. I'm using the below code to send and consume message.
#ApplicationScoped
public class RabbitMQProducerAdapterImpl extends RabbitMQCongiguration implements RabbitMQProducerAdapter {
#Override
public void sendMessage(String exchange, String routingKey, String messagePayload) throws IOException {
setUpConnectionAndChannel();
channel.basicPublish(exchange, routingKey, null, messagePayload.getBytes(StandardCharsets.UTF_8));
Log.info("message sent succefully: " + messagePayload);
}
}
Here is the RabbitMQCongiguration
#ApplicationScoped
public class RabbitMQCongiguration {
#Inject
private RabbitMQClient rabbitClient;
protected Channel channel;
protected void setUpConnectionAndChannel() {
try {
// create a connection
Connection connection = rabbitClient.connect();
// create a channel
channel = connection.createChannel();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
protected void setupQueueInDirectExchange(String exchangeName, String routingKey, String queueName,
boolean createExchangeQueues) throws IOException {
setUpConnectionAndChannel();
if (createExchangeQueues) {
this.channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, false, null);
// declaring a queue for this channel. If queue does not exist,
// it will be created on the server. this line not needed if queue already
// present
this.channel.queueDeclare(queueName, true, false, false, null);
}
// Bind Routing Key to Exchange
this.channel.queueBind(queueName, exchangeName, routingKey);
}
}
Below is the class for consumer
#ApplicationScoped
public class RabbitMQConsumerAdapterImpl extends RabbitMQCongiguration implements RabbitMQConsumerAdapter, Runnable {
private String queueName;
private MessageProcessor messageProcessor;
#Override
public void consumeMessage(String exchange, String queueName, String routingKey,
MessageProcessor messageProcessor) throws IOException {
Log.info("starting consumer...");
try {
this.queueName = queueName;
this.messageProcessor = messageProcessor;
Log.info("setting up rabbitMQPrefetchCountConfig");
setupQueueInDirectExchange(exchange, routingKey, queueName, false);
Thread consumerThread = new Thread(this);
consumerThread.start();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
#Override
public void run() {
try {
// start consuming messages. Auto acknowledge messages.
Log.info("Start consuming messages from thread...");
channel.basicConsume(this.queueName, false, (Consumer) new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String msgPayload = null;
if (body == null || body.length == 0) {
Log.warn("Invalid Message Body - Consumer Tag : " + consumerTag + ", Message DeliveryTag : "
+ envelope.getDeliveryTag());
channel.basicReject(envelope.getDeliveryTag(), false);
} else {
msgPayload = new String(body);
try {
JsonParser.parseString(msgPayload);
} catch (JsonSyntaxException ex) {
Log.error(msgPayload + " is not a valid json, Reason - ", ex);
channel.basicReject(envelope.getDeliveryTag(), false);
Log.warn("Rejected the current payload.");
return;
}
messageProcessor.processMessage(msgPayload);
channel.basicAck(envelope.getDeliveryTag(), false);
}
// just print the received message.
Log.info("Received: " + new String(body, StandardCharsets.UTF_8));
}
});
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
#ApplicationScoped
public class MessageProcessorImpl implements MessageProcessor{
#Override
public void processMessage(String messagePayload) {
Log.info("message consumed: " + messagePayload);
}
}

rxjava - How to handle merge exceptions without terminating the whole process

I have created two observables.
One of them throws an exception.
obs1 = Observable.from(new Integer[]{1, 2, 3, 4, 5, 6});
obs2 = Observable.create(new Observable.OnSubscribe<Integer>() {
#Override public void call(Subscriber<? super Integer> subscriber) {
boolean b = getObj().equals(""); // this throws an exception
System.out.println("1");
}
});
Now I invoke them using
Observable.merge(obs2, obs1)
.subscribe(new Observer<Integer>() {
#Override public void onCompleted() {
System.out.println("onCompleted");
}
#Override public void onError(Throwable throwable) {
System.out.println("onError");
}
#Override public void onNext(Integer integer) {
System.out.println("onNext - " + integer);
}
});
Now, I dont want my process to halt completely when an exception occurs -
I want to handle it and I want obs1 to continue its work.
I have tried to write it using onErrorResumeNext(), onExceptionResumeNext(), doOnError()
but nothing helped - obs1 did not run.
How can I handle the exception without stopping the other observable from being processed?
Sounds like you need mergeDelayError.
The problem is in your subscriber which is broken. You should catch your exception and call onError. Otherwise, you broke the rx contract.
example :
Observable<Integer> obs1 = Observable.from(Arrays.asList(1, 2, 3, 4, 5, 6));
Observable<Integer> obs2 = Observable.create((Subscriber<? super Integer> subscriber) -> {
subscriber.onError(new NullPointerException());
});
Observable.merge(obs2.onErrorResumeNext((e) -> Observable.empty()), obs1)
.subscribe(new Observer<Integer>() {
#Override public void onCompleted() {
System.out.println("onCompleted");
}
#Override public void onError(Throwable throwable) {
System.out.println("onError");
}
#Override public void onNext(Integer integer) {
System.out.println("onNext - " + integer);
}
});
so if you replace your obs2 code with this, it should work like you expected :
obs2 = Observable.create(new Observable.OnSubscribe<Integer>() {
#Override public void call(Subscriber<? super Integer> subscriber) {
try {
boolean b = getObj().equals(""); // this throws an exception
System.out.println("1");
} catch(Exception ex) {
subscriber.onError(ex);
}
}
});

JList lazy load images

since i am not a java swing expert i need some help to understand why my images in my JList do not appear.
I have a JList that pops up containing all products (with inline pictures) while the user enters a search criteria. The results come from lucene and will be rendered in a JList in real time.
To lazy load the inline product images i am using a swingworker inside my rendering class.
Any help would be great!
public abstract class MatchRenderer implements ListCellRenderer {
#Override
public Component getListCellRendererComponent(JList list, final Object value, int index,
boolean isSelected, boolean cellHasFocus) {
Component component = defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (quickRenderMode) {
return component;
} else {
try {
component = renderHook(value, component);
} catch (Exception e) {
System.err.println("Search string: " + searchString);
System.err.println(value.toString());
e.printStackTrace();
}
JPanel itemPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel label = new JLabel(defaultIcon, SwingConstants.HORIZONTAL);
itemPanel.add(label);
itemPanel.add(component);
if (value instanceof QoogleEntity && ((QoogleEntity) value).isProduct()) {
QoogleEntity qoogleItem = (QoogleEntity) value;
String imageUrl = qoogleItem.getQInfos().get(0).getqValue();
//LAZY LOAD STARTS HERE...
new ImageRetriever(label, imageUrl).execute();
}
return itemPanel;
}
}
protected abstract Component renderHook(Object value, Component component);
class ImageRetriever extends SwingWorker<ImageIcon, String> {
private JLabel lbImage;
private String imageUrl;
public ImageRetriever(JLabel lbImage, String imageUrl) {
this.lbImage = lbImage;
this.imageUrl = imageUrl;
}
#Override
protected void done() {
try {
lbImage.setIcon(get());
lbImage.repaint();
} catch (Exception e) {
}
}
#Override
protected ImageIcon doInBackground() throws Exception {
return ImageLoader.loadImageFromUrl(imageUrl, 80, 80);
}
};

null pointer exception at run time

This is my first post here. I am trying to create a singly link list. I am using AtEnd and AtStart methods to insert values at the end or in the beginning of the list and using display method to print all the values. The insertion methods seems to be working fine (at least I think so) but whenever I call display method it shows only the first value and then there is a null pointer exception. For example when I run this code I see only 9 and then there is the NPE despite the fact that I have put a check on the display method for "not null".
class node {
private int data;
private node next;
node() {
}
node(int data) {
this.data = data;
this.next = null;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data=data;
}
public node getNext() {
return next;
}
public void setNext(node next) {
this.next = next;
}
}
public class list extends node {
node head;
list() {
}
public void AtStart(int val) {
node n = new node(val);
if (head == null) {
head=n;
} else {
n.setNext(head);
int temp = head.getData();
head.setData(val);
n.setData(temp);
//n = head;
}
}
public void AtEnd(int val) {
if (head == null) {
node n = new node(val);
head = n;
} else {
node t = head;
for(; t.getNext() != null; ) {
if(t.getNext() == null) {
t.setNext(new node (val));
}
t = t.getNext();
}
}
}
public void display() {
node t = head;
for(; t.getNext() == null;) {
if (t !=null) {
System.out.println(t.getData());
t = t.getNext();
}
}
}
}
public static void main(String args[]) {
list l = new list();
l.AtStart(16);
l.AtEnd(6);
l.AtEnd(36);
l.AtStart(9);
l.AtEnd(22);
l.display();
}
i dont get what your AtStart function does, it should be much simpler:
public void AtStart(int val){
if(head==null){
head=n;
}
else{
head.setnext(head);
head.setData(val);
}
}