|
Replies:
13
-
Last Post:
Jul 8, 2008 5:52 PM
by: i30817
|
|
|
|
|
|
|
Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 4, 2008 4:39 PM
|
|
|
It's so very inconsistent and complex and incomplete and it gives me the shivers.
The nightmare that is AbstractUndoableEdit a abstract class that has no abstract methods (no empty methods too), and needs to call super.undo() or super.redo() to function correctly.
In fact the whole undo redo mechanism...
The incomplete html parser that at least follows a visitor like pattern. The rtf and text parser that emphatically do not.
The editorkit mechanism that binds the stylededitorkit to a concrete class (JEditorPane).
The justification mechanism that doesn't allow configuration for justified text in the last line of a paragraph (it is always not justified).
The clumsy way to insert components into text.
The (deprecated) enumeration interface.
This is just a ranting thread. If you have a part of the sdk you especially dislike rant here. Who knows, maybe it will provide impetus for the creation of sane replacements.
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 4, 2008 6:29 PM
in response to: i30817
|
|
|
> The nightmare that is AbstractUndoableEdit a abstract > class that has no abstract methods (no empty methods > too). > > In fact the whole undo redo mechanism... I have used the undo/redo mechanism and have not had any trouble with it, other than the fact that most of the components that I want to undo/redo, should have this build in. Perhaps, this could be an line of improvement for SwingX: better undo/redo integration.
> The incomplete html parser that at least follows a > visitor like pattern. Yep, that parser is problematic. At least, the parsers are replaceable, but I've never seen anyone bother to replace them with better versions. Everyone seems to work around the problems.
> The clumsy way to insert components into text. Hmm? What do you mean by this?
As for my own ranting, I posted a thread some time ago about poor design choices with JMenu that I'd like to remind everyone about. Yeah, JMenu is less than stellar.
I would like to try to get us to propose some fixes/directions that we can take SwingX in to solve some of these pain points; that's what SwingX is all about, after all.
Karl
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 4, 2008 9:34 PM
in response to: kschaefe
|
|
|
Since i've been using UndoManager right now, i will tell what do i think it's wrong with it:
The redo and undo methods on AbstractUndoableEdit, should be abstract and called internally - the way it is setup now requires that the implementers call super() - HELLO! 1997 wants it's pattern back.
The UndoManager is not observable! Something that would be very useful for, i don't know, undo and redo buttons!
... The only way to embed a component in a swing text document is (without layout) with a property in a CharacterElement or ParagraphElement (i can't recall).
This is used to display images in a Document.
Message was edited by: i30817
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 6, 2008 3:33 AM
in response to: i30817
|
|
|
Yeah, maybe it doesn't have the best API. How about you post the runnable example that highlights the problems with it so we can discuss the issue based on concrete example and figure out how to make it better? Thanks, Jan
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 6, 2008 6:01 PM
in response to: rah003
|
|
|
A complete running example is a lot of work for small gain here, i will just say my problem and my solution (and horrible code): My problem: as there are undo and redo buttons in my program, there should be a way that their enabled state is set as a result of any UndoManager alteration. Solution: make UndoManager observable. In the following code, i wasn't sure of the appropriate events to put in the firePropertyChange so i but holder values. I also used a factory abstract class i made to allow me to create different types of UndoableEdits on the undoManager itself. I probably should add a createAndAdd method.
This doesn't solve the UndoableEdit weirdness yet. Abstract classes with only real functions are evil simply because auto completion in IDEs just doesn't give you the way to go.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package util;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
/**
*
* @author i30817
*/
public class DefaultUndoManager<ARGUMENT> extends UndoManager {
PropertyChangeSupport prop = new PropertyChangeSupport(this);
Factory<UndoableEdit, ARGUMENT> factory;
public DefaultUndoManager(Factory<UndoableEdit, ARGUMENT> factory) {
this.factory = factory;
}
public UndoableEdit getUndoableEdit(ARGUMENT arg) {
if (!factory.accepts(arg)) {
throw new IllegalArgumentException("Illegal factory argument.");
}
return factory.create(arg);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
prop.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
prop.removePropertyChangeListener(l);
}
@Override
public boolean addEdit(UndoableEdit edit) {
boolean r = super.addEdit(edit);
prop.firePropertyChange("", "1", "2");
return r;
}
@Override
public boolean replaceEdit(UndoableEdit edit) {
boolean r = super.replaceEdit(edit);
prop.firePropertyChange("", "1", "2");
return r;
}
@Override
public void discardAllEdits() {
super.discardAllEdits();
prop.firePropertyChange("", "1", "2");
}
@Override
public void undoTo(UndoableEdit edit) {
super.undoTo(edit);
prop.firePropertyChange("", "1", "2");
}
@Override
public void undo() {
super.undo();
prop.firePropertyChange("", "1", "2");
}
@Override
public void redo() {
super.redo();
prop.firePropertyChange("", "1", "2");
}
@Override
public void trimForLimit() {
super.trimForLimit();
prop.firePropertyChange("", "1", "2");
}
@Override
public void trimEdits(int from, int to) {
super.trimEdits(from, to);
prop.firePropertyChange("", "1", "2");
}
@Override
public void redoTo(UndoableEdit edit) {
super.redoTo(edit);
prop.firePropertyChange("", "1", "2");
}
}
Used like this, T2 is a tuple of two:
private DefaultUndoManager<T2<Integer, Integer>> undoRedo = new DefaultUndoManager(
new Factory<UndoableEdit, T2<Integer, Integer>>() {
@Override
public UndoableEdit create(final T2<Integer, Integer> arg) {
return new MovementUndoableEdit(arg.getFirst(), arg.getSecond());
}
});
I'm pretty sure that swingx wont want to get into this functional swamp, but the event notification at least is worthwhile - if significant old and new values can be found.
Oh and i also have another irritation with AbstractDocument this time. The only way to create a document efficiently when you have loads of text is to avoid the document lock (stupid too clever design btw) and use the protected bulk insert method. Subclass or Reflection time! Since i got bored from this, i created his little class, that buffers the work, using reflection when it must or my little document hacked subclass when it can. Horrible code ahead. The random access remove is a lost cause (slow) and the removeEnd method shouldn't exist (a unavoidable? hack). And the insert methods should be called append.
Since this code is horrible beyond relief i would just counsel that, if you want something like this is swingx, just replace AbstractDocument in the swingx text components to have the protected method public and add a builder that does something similar to this without the insanity.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package util.swing;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.DefaultStyledDocument.ElementSpec;
import javax.swing.text.Element;
/**
* A fast buffered builder for creating DefaultStyledDocuments
* @author i30817
*/
public class BufferedStyledDocumentBuilder {
private final long MEMORY_CAPACITY_CHARS;
private int bufferedChars;
private final LinkedList<ElementSpec> textList = new LinkedList<ElementSpec>();
private final char[] par = {'\n'};
private final char[] space = {' '};
private final DefaultStyledDocument doc;
private final Insert insertFunctor;
private interface Insert {
void insert();
}
private final class InsertStyledDocument implements Insert {
private final Method bulkInsert;
private final Object[] bulkInsertArgs;
public InsertStyledDocument() {
try {
Class[] args = new Class[]{Integer.TYPE, new ElementSpec[]{}.getClass()};
bulkInsertArgs = new Object[2];
bulkInsert = DefaultStyledDocument.class.getDeclaredMethod("insert", args);
//naughty
bulkInsert.setAccessible(true);
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
}
public void insert() {
ElementSpec[] arr = new ElementSpec[textList.size()];
bulkInsertArgs[0] = doc.getLength();
bulkInsertArgs[1] = textList.toArray(arr);
try {
bulkInsert.invoke(doc, bulkInsertArgs);
} catch (Exception ex) {
//Should never happen but whatever
throw new RuntimeException(ex);
}
}
}
private final class InsertMyStyledDocument implements Insert {
private final Method bulkInsert;
private final Object[] bulkInsertArgs;
public InsertMyStyledDocument() {
try {
Class[] args = new Class[]{Integer.TYPE, new ElementSpec[]{}.getClass(), Integer.TYPE};
bulkInsertArgs = new Object[3];
bulkInsert = Class.forName("ui.documents.MyStyledDocument").getMethod("insert", args);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void insert() {
ElementSpec[] arr = new ElementSpec[textList.size()];
bulkInsertArgs[0] = doc.getLength();
bulkInsertArgs[1] = textList.toArray(arr);
bulkInsertArgs[2] = bufferedChars;
try {
bulkInsert.invoke(doc, bulkInsertArgs);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
public BufferedStyledDocumentBuilder(DefaultStyledDocument d) {
doc = d;
//convert the free memory from bytes to dbytes (char size) and quarter it.
MEMORY_CAPACITY_CHARS = (int) (Runtime.getRuntime().freeMemory() / 2) / 4;
//non-dependent runtime testing if it is my subclass
boolean isMyClass = false;
try {
isMyClass = Class.forName("ui.documents.MyStyledDocument").isInstance(d);
} catch (Throwable e) {
//do nothing...
}
if (isMyClass) {
insertFunctor = new InsertMyStyledDocument();
} else {
insertFunctor = new InsertStyledDocument();
}
}
public Integer getLength() {
return Integer.valueOf(doc.getLength() + bufferedChars);
}
public DefaultStyledDocument getDocument() {
return doc;
}
public void clear() {
try {
doc.remove(0, doc.getLength());
bufferedChars = 0;
textList.clear();
} catch (BadLocationException ex) {
throw new RuntimeException(ex);
}
}
//Far too inefficent
// public void remove(int index, int len) {
// try {
//
// if ((index + len) <= doc.getLength()) {
// doc.remove(index, len);
// } else if (index <= doc.getLength()) {
// int docLen = doc.getLength() - index;
// int bufferLen = len - docLen;
// doc.remove(index, docLen);
// removeRangeFromBuffer(0, bufferLen);
// } else {
// removeRangeFromBuffer(index, len);
// }
//
// } catch (BadLocationException ex) {
// ex.printStackTrace();
// }
// }
//
// private void removeRangeFromBuffer(int index, int len) {
// if (len > 0) {
// ListIterator<ElementSpec> i = textList.listIterator();
//
// ElementSpec firstInRange = null, lastInRange = null;
// int acc = 0, firstInRangeStartIndex = 0, lastInRangeEndIndex = 0, endIndex = index + len;
//
// while (i.hasNext()) {
// ElementSpec current = i.next();
// acc += current.getLength();
//
// if (index <= acc && endIndex >= acc) {
// if (firstInRange == null) {
// firstInRangeStartIndex = index - (acc - current.getLength());
// firstInRange = current;
// }
// bufferedChars -= current.getLength();
// i.remove();
// } else if (endIndex < acc) {
// lastInRangeEndIndex = endIndex - (acc - current.getLength());
// lastInRange = current;
// bufferedChars -= current.getLength();
// i.remove();
// break;
// }
// }
//
// if (firstInRange != null && firstInRangeStartIndex != 0) {
// ElementSpec s = new ElementSpec(firstInRange.getAttributes(), ElementSpec.ContentType, firstInRange.getArray(), 0, firstInRangeStartIndex);
// i.add(s);
// bufferedChars += s.getLength();
// }
// if (lastInRange != null && lastInRangeEndIndex != lastInRange.getLength()) {
// ElementSpec s = new ElementSpec(lastInRange.getAttributes(), ElementSpec.ContentType, lastInRange.getArray(), lastInRangeEndIndex, lastInRange.getLength() - lastInRangeEndIndex);
// i.add(s);
// bufferedChars += s.getLength();
// }
// }
// }
/**
* Removes the last inserted \n.
* No checking so you must know you inserted a \n by insertEnd before.
*/
public void removeEnd() {
if(textList.isEmpty()){
try {
doc.remove(doc.getLength() - 1, 1);
} catch (BadLocationException ex) {
throw new RuntimeException(ex);
}
}else{
textList.removeLast();
textList.removeLast();
textList.removeLast();
bufferedChars = bufferedChars - 1;
}
}
public void insertSpace(AttributeSet currentAttributes) {
insert(space, currentAttributes);
}
public void insertEnd(AttributeSet currentAttributes) {
bufferedChars += 1;
ElementSpec e = new ElementSpec(currentAttributes.copyAttributes(), ElementSpec.ContentType, par, 0, 1);
textList.add(e);
e = new ElementSpec(null, ElementSpec.EndTagType);
textList.add(e);
//Every non first Element needs a start type so the stack based document tree doesn't go bonkers
//Paragraph element is aleatory, using the first element to reuse the object.
Element paragraph = doc.getParagraphElement(0);
AttributeSet pattr = paragraph.getAttributes();
e = new ElementSpec(pattr, ElementSpec.StartTagType);
textList.add(e);
}
public void insert(char[] s, AttributeSet currentAttributes) {
textList.add(new ElementSpec(currentAttributes.copyAttributes(), ElementSpec.ContentType, s, 0, s.length));
bufferedChars += s.length;
if (bufferedChars > MEMORY_CAPACITY_CHARS) {
commit();
}
}
public void insert(char[] s, int len, AttributeSet currentAttributes) {
textList.add(new ElementSpec(currentAttributes.copyAttributes(), ElementSpec.ContentType, s, 0, len));
bufferedChars += len;
if (bufferedChars > MEMORY_CAPACITY_CHARS) {
commit();
}
}
public void commit() {
insertFunctor.insert();
bufferedChars = 0;
textList.clear();
}
}
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 7, 2008 9:54 AM
in response to: i30817
|
|
|
Welcome to the world of Swing. Of course Sun knows about the deficits since a decade (see, for example, the JavaDoc for the RTF parser). Like they know the file chooser is broken, that JTable is broken, that the font handling is broken, that the Media Framework is broken, that Java 3D is broken, that desktop integration is broken, or that the Action system is broken. To cite just a few.
> Who knows, maybe it will provide impetus for the creation of sane replacements.
Not from Sun. They had more than a decade to fix things. They don't care. In the past junk like generics was more important, in the future junk like closures and JavaFX are more important for Sun.
You are better off writing your own replacement of looking for third-party replacements.
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 7, 2008 11:16 AM
in response to: ewin
|
|
|
Man - this is a "third party". Swingx doesn't need to obey the backwards compatibility god. So it is a good policy to vent the frustration areas of the less commonly used areas of swing here.
Btw why does PropertyChangeSupport has no simple fireChanges() method for "unknown" changes? Forget it. Probably because that is meaningless anyway.
How about using the (old and new) size of the UndoableEdit collection as the changed property in the UndoManager methods?
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 7, 2008 12:01 PM
in response to: i30817
|
|
|
Thanks for the example. I'll try to go through the code when I get a few hours of free time without interuption. BTW, if you have idea for the solution or how it can be supported better by SwingX feel free to put your proposal in the incubator to make people look at it.
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 7, 2008 12:03 PM
in response to: ewin
|
|
|
You can look at those problems from the other side - jdk and all the other frameworks are now open sourced so you are more then welcome to submit the patches and see them applied there. It would be definitively more helpful then whining about the problems.
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 7, 2008 11:57 PM
in response to: rah003
|
|
|
> You can look at those problems from the other side - > jdk and all the other frameworks are now open sourced > so you are more then welcome to submit the patches > and see them applied there.
No, I can't. There is this lawyer excr*ment, called the Sun Contributor Agreement (SCA) which is in between. I won't sign that. Sun has long promised (yet another broken Sun promise) to change it.
Second, there is a need to find a "sponsor" to get your work accepted. I won't s*ck the d*ck of a "sponsor". In particular not, because the "sponsor" will come from the same group of Sun developers that let rot Swing and are responsible for its current state. So they have zero interest in fixing anything. They had more than ten years to do so.
Finally, I won't work for free for Sun. Especially not because they sponsored SCO's anti open-source crusade (and don't give me that "we bought drives" excuse).
> It would be definitively > more helpful then whining about the problems.
You mean like working on SwingX in SwingLabs for how many years now? Didn't they start around Java 1.4? I haven't seen the core of SwingX in any SE yet. How many years of development and not getting into the whole thing into the main SE track? And they don't even need a "sponsor".
The point is, either Sun finally gets its act together and starts working on Swing like professionals, or Java on the desktop is dead. No third-party software like SwingX can rescue it. It needs to be part of Java and come with SE. Sun has to lead the way (they don't with JavaFX), and Sun has to do the work. They need to demonstrate that they finally take the desktop serious, by working on it, and not just paying lip service. If Sun doesn't demonstrate it, by doing real work, how should contributors believe they are doing something valuable, something welcome, something long lasting?
As long as Java desktop work at Sun is dominated by the programmer equivalent of Vegas showgirls, eager to do the presentation dance on every conference that comes along, shaking their big powerpoint slides, and hoping big daddy Google will put them out of their misery by offering a job, nothing will happen. This is something Sun's management needs to address, but doesn't.
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 8, 2008 12:12 AM
in response to: ewin
|
|
|
ewin,
Your comments are far from constructive.
You complain that Sun won't let you contribute unreviewed code, which hardly makes sense for a commercial product.
You complain that sun won't let you contribute code without signing the SCA yet you don't explain how Sun is supposed to address code-relicensing problems (for example, moving from GPL2 to GPL3). It is unrealistic to expect them to contact thousands of people if they ever need to relicense the code. Plenty of other companies do the same as Sun, for a good reason. On the flip side, there are plenty of projects that did not do this and died as a result. If the source-code license becomes an issue sometime in the future (as it has in the recent past) you'll be thanking your lucky stars that Sun can modify the license without months' worth of work.
You write your won't "work for free for Sun" yet you (and other open-source zealots) expected Sun to contribute over a decade's worth of source-code for free. Sun contributed literally millions of dollar's worth of source-code (in terms of cost, not mentioning any profits here). I see absolutely nothing wrong with them riding open-source contributions. If it's good enough for other OSS companies why isn't it good enough for Sun? I seriously question if there is *anything* Sun can do to get OSS zealots to stop criticizing Sun. You want your cake and eat it too. Life just doesn't work that way, sorry.
I agree that Swing needs some serious work, but I take serious issue with your attitude. Spewing profanities and insulting people will get you nowhere fast. Just my 2 cents.
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 7, 2008 9:10 PM
in response to: i30817
|
|
|
While we're on the topic of ranting... I find pretty much most/all of Swing to be a nightmare to code with.
1) The API is full of inconsistent quirks not explained in the Javadoc. I almost always turn to http://exampledepot.com/ to figure out how to do stuff because it isn't obvious from the code.
2) The code is a big spaghetti mess with undocumented dependencies. Fixing Swing bugs or trying to extend the classes is a total nightmare.
Swing might be a great improvement over earlier APIs but I think it's time to refactor it: simplify the API, make it more consistent and clean up the implementation. There are literally thousands of Swing bugs that will never get fixed because of the state of the current code.
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 8, 2008 12:05 PM
in response to: i30817
|
|
|
Hi i30817 !
I have worked with undo/redo a little. Here's my 2 cents on one thing you mentioned...
>>>The redo and undo ....requires that the implementers call super() -
I think this is because internally, redo and undo effect the state machine of the whole Undo framework and that state is something that developers don't implement- it's implemented for us. But to remain consistent, there can't be any undoing and redoing going on behind that state machine's back. When you call super, you are making sure the state machine knows everything it needs to know in order to remain consistent.
>>>The UndoManager is not observable! Something that would be very useful for, i don't know, undo and redo buttons!
This is certainly an approach that would work. The way it's done, all undo and redo implementations can hook into any notification framework (like beans binding) that they want to. That's what I do. since I am writing the code to execute the undo (define it) then I also notify who it is that needs to be notified.
If UndoManager were observable, then observers would have to register with it. But that would be done with very generic types, and lots of casting. If every time the UndoManager undid something it starting notifying everyone, then that's a lot of notifications to entities that probably just don't care. If you fix that problem by adding "notify if condition == true" type logic, as most notification framework have, then you have a lot of responsibility on the UndoManager and it's getting pretty unfocused.
undoManager's job is to keep things straight so the undo/redo logic comes out right, whatever the undo redo functions may do as far as their exact effect on a program.
cheers!!!
|
|
|
|
|
|
|
|
Re: Does anyone else thinks that swing.text is the worst part of swing?
Posted:
Jul 8, 2008 4:29 PM
in response to: swv
|
|
|
I am not convinced about the need to call super, simply because it is in the AbstractUndoableEdit.
I would imagine that it would be pretty easy for the UndoManager itself to make that operation itself by package private api.
This would have the advantage of being possible to mark undo and redo abstract on the AbstractUndoableEdit and making IDE auto completion guide users.
Still don't agree with the lack of notification, but can understand it in the context offered. BTW when i was thinking about this yesterday i figured this is just a specific usecase of something larger: observable collections. Maybe - that - should be in the jdk.
|
|
|
|
|