diff --git a/src/main/java/app/INavigator.java b/src/main/java/app/INavigator.java index 2ff04bb..a95c857 100644 --- a/src/main/java/app/INavigator.java +++ b/src/main/java/app/INavigator.java @@ -13,4 +13,5 @@ public interface INavigator { void showIdealFiltering(); void showButterworth(); void showConvolutionFiltering(); + void showBasicMorphology(); } diff --git a/src/main/java/app/SwingNavigator.java b/src/main/java/app/SwingNavigator.java index 7caf564..47f7065 100644 --- a/src/main/java/app/SwingNavigator.java +++ b/src/main/java/app/SwingNavigator.java @@ -2,11 +2,13 @@ package app; import com.google.inject.Inject; import com.google.inject.Provider; +import ui.abstractions.AbstractView; import ui.implementation.views.DoubleMatrix; import ui.implementation.views.fitering.global.IdealFilteringView; import ui.implementation.views.fitering.global.NonIdealFilteringView; import ui.implementation.views.ViewInitializer; import ui.implementation.views.fitering.local.LocalFilteringView; +import ui.implementation.views.morphology.BasicMorphologyView; /** * Concrete implement of the INavigator interface. @@ -19,46 +21,55 @@ public class SwingNavigator implements INavigator { private final Provider filteringProvider; private final Provider nonIdealFilteringProvider; private final Provider convolutionFilteringProvider; + private final Provider basicMorphologyProvider; @Inject public SwingNavigator( Provider doubleMatrixProvider, Provider filteringProvider, Provider nonIdealFilteringProvider, - Provider convolutionFilterProvider + Provider convolutionFilterProvider, + Provider basicMorphologyProvider ) { this.doubleMatrixProvider = doubleMatrixProvider; this.filteringProvider = filteringProvider; this.nonIdealFilteringProvider = nonIdealFilteringProvider; this.convolutionFilteringProvider = convolutionFilterProvider; + this.basicMorphologyProvider = basicMorphologyProvider; } @Override public void showFourier() { DoubleMatrix view = doubleMatrixProvider.get(); - ViewInitializer.Initialize(view); - view.setVisible(true); + runView(view); } @Override public void showIdealFiltering() { IdealFilteringView view = filteringProvider.get(); - ViewInitializer.Initialize(view); - view.setVisible(true); + runView(view); } @Override public void showButterworth() { NonIdealFilteringView view = nonIdealFilteringProvider.get(); - ViewInitializer.Initialize(view); - view.setVisible(true); + runView(view); } @Override public void showConvolutionFiltering() { LocalFilteringView view = convolutionFilteringProvider.get(); + runView(view); + } + + @Override + public void showBasicMorphology() { + BasicMorphologyView view = basicMorphologyProvider.get(); + runView(view); + } + + private void runView(AbstractView view) { ViewInitializer.Initialize(view); view.setVisible(true); } - } diff --git a/src/main/java/app/state/MorphologyState.java b/src/main/java/app/state/MorphologyState.java new file mode 100644 index 0000000..1aecd50 --- /dev/null +++ b/src/main/java/app/state/MorphologyState.java @@ -0,0 +1,65 @@ +package app.state; + +import com.google.inject.Inject; +import domain.image.Image; +import presenters.morphology.MorphologyListener; + +import java.util.ArrayList; +import java.util.List; + +public class MorphologyState extends State { + + private Image erosion; + private Image dilation; + private Image opening; + private Image closing; + + private final List listeners = new ArrayList<>(); + + @Inject + public MorphologyState() {} + + public void subscribe(MorphologyListener listener) { + listeners.add(listener); + } + + public void unsubscribe(MorphologyListener listener) { + listeners.remove(listener); + } + + public void setErosion(Image image) { + erosion = image; + listeners.forEach(listener -> listener.onErosionChanged(image)); + } + + public void setDilation(Image image) { + dilation = image; + listeners.forEach(listener -> listener.onDilationChanged(image)); + } + + public void setOpening(Image image) { + opening = image; + listeners.forEach(listener -> listener.onOpeningChanged(image)); + } + + public void setClosing(Image image) { + closing = image; + listeners.forEach(listener -> listener.onClosingChanged(image)); + } + + public Image getErosion() { + return erosion; + } + + public Image getDilation() { + return dilation; + } + + public Image getOpening() { + return opening; + } + + public Image getClosing() { + return closing; + } +} \ No newline at end of file diff --git a/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java index dc89576..d0f52b6 100644 --- a/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java +++ b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java @@ -1,15 +1,10 @@ package domain.image.processing.filtering; import domain.image.GrayScaleMatrix; -import domain.image.Image; -import domain.image.Pixel; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; -import java.util.ArrayList; -import java.util.List; - public class GlobalLinearFiltering { public static GrayScaleMatrix lowPassFiltering(GrayScaleMatrix matrix, double cutoff) { diff --git a/src/main/java/domain/image/processing/morphology/BasicMorphology.java b/src/main/java/domain/image/processing/morphology/BasicMorphology.java new file mode 100644 index 0000000..1c45cdd --- /dev/null +++ b/src/main/java/domain/image/processing/morphology/BasicMorphology.java @@ -0,0 +1,101 @@ +package domain.image.processing.morphology; + +import domain.image.GrayScaleMatrix; +import domain.image.Image; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; + +public class BasicMorphology { + + public static GrayScaleMatrix erosion(Image image, int tailleMasque) { + Mat input = toMat(image); + Mat kernel = createKernel(tailleMasque); + Mat result = new Mat(); + + Imgproc.erode(input, result, kernel); + + return toGrayScaleMatrix(result); + } + + public static GrayScaleMatrix dilation(Image image, int tailleMasque) { + Mat input = toMat(image); + Mat kernel = createKernel(tailleMasque); + Mat result = new Mat(); + + Imgproc.dilate(input, result, kernel); + + return toGrayScaleMatrix(result); + } + + public static GrayScaleMatrix opening(Image image, int tailleMasque) { + Mat input = toMat(image); + Mat kernel = createKernel(tailleMasque); + Mat result = new Mat(); + + Imgproc.morphologyEx( + input, + result, + Imgproc.MORPH_OPEN, + kernel + ); + + return toGrayScaleMatrix(result); + } + + public static GrayScaleMatrix closing(Image image, int tailleMasque) { + Mat input = toMat(image); + Mat kernel = createKernel(tailleMasque); + Mat result = new Mat(); + + Imgproc.morphologyEx( + input, + result, + Imgproc.MORPH_CLOSE, + kernel + ); + + return toGrayScaleMatrix(result); + } + + private static Mat toMat(Image image) { + GrayScaleMatrix gray = image.toGrayScale(); + + int width = gray.getWidth(); + int height = gray.getHeight(); + + Mat mat = new Mat(height, width, CvType.CV_64F); + + double[][] data = gray.getRawData(); + + for (int y = 0; y < height; y++) { + mat.put(y, 0, data[y]); + } + + return mat; + } + + private static Mat createKernel(int tailleMasque) { + return Imgproc.getStructuringElement( + Imgproc.MORPH_RECT, + new Size(tailleMasque, tailleMasque) + ); + } + + private static GrayScaleMatrix toGrayScaleMatrix(Mat mat) { + int width = mat.cols(); + int height = mat.rows(); + + double[][] output = new double[height][width]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + output[y][x] = mat.get(y, x)[0]; + } + } + + return new GrayScaleMatrix(output); + } + +} diff --git a/src/main/java/domain/image/processing/morphology/ComplexMorphology.java b/src/main/java/domain/image/processing/morphology/ComplexMorphology.java new file mode 100644 index 0000000..c5fcc7e --- /dev/null +++ b/src/main/java/domain/image/processing/morphology/ComplexMorphology.java @@ -0,0 +1,96 @@ +package domain.image.processing.morphology; + +import domain.image.GrayScaleMatrix; +import domain.image.Image; +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.imgproc.Imgproc; + +public class ComplexMorphology { + + public static GrayScaleMatrix geodesicDilation(Image image, Image mask, int size) { + Mat input = toMat(image); + Mat maskMat = toMat(mask); + Mat kernel = createKernel(size); + + Mat dilated = new Mat(); + Imgproc.dilate(input, dilated, kernel); + + Mat result = new Mat(); + Core.min(dilated, maskMat, result); + + return toGrayScaleMatrix(result); + } + + public static GrayScaleMatrix geodesicReconstruction(Image marker, Image mask, int size) { + Mat markerMat = toMat(marker); + Mat maskMat = toMat(mask); + Mat kernel = createKernel(size); + + Mat current = markerMat.clone(); + + while (true) { + Mat dilated = new Mat(); + Imgproc.dilate(current, dilated, kernel); + + Mat next = new Mat(); + Core.min(dilated, maskMat, next); + + if (Core.norm(next, current, Core.NORM_INF) == 0) { + return toGrayScaleMatrix(next); + } + + current = next; + } + } + + public static GrayScaleMatrix meanFilter(Image image, int size) { + Mat input = toMat(image); + Mat result = new Mat(); + + Imgproc.blur(input, result, new Size(size, size)); + + return toGrayScaleMatrix(result); + } + + private static Mat toMat(Image image) { + GrayScaleMatrix gray = image.toGrayScale(); + + int width = gray.getWidth(); + int height = gray.getHeight(); + + Mat mat = new Mat(height, width, CvType.CV_64F); + + double[][] data = gray.getRawData(); + + for (int y = 0; y < height; y++) { + mat.put(y, 0, data[y]); + } + + return mat; + } + + private static Mat createKernel(int size) { + return Imgproc.getStructuringElement( + Imgproc.MORPH_RECT, + new Size(size, size) + ); + } + + private static GrayScaleMatrix toGrayScaleMatrix(Mat mat) { + int width = mat.cols(); + int height = mat.rows(); + + double[][] output = new double[height][width]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + output[y][x] = mat.get(y, x)[0]; + } + } + + return new GrayScaleMatrix(output); + } +} \ No newline at end of file diff --git a/src/main/java/presenters/NavPresenter.java b/src/main/java/presenters/NavPresenter.java index 651831d..eb02cb6 100644 --- a/src/main/java/presenters/NavPresenter.java +++ b/src/main/java/presenters/NavPresenter.java @@ -42,28 +42,18 @@ public class NavPresenter { appState.setImage(image); } - public void goToFourier() { - navigator.showFourier(); - } + public void goToFourier() { navigator.showFourier(); } - public void goToFiltering() { - navigator.showIdealFiltering(); - } + public void goToFiltering() { navigator.showIdealFiltering(); } - public void goToConvolution() { - navigator.showConvolutionFiltering(); - } + public void goToConvolution() { navigator.showConvolutionFiltering(); } - public void gotToButterworth() { - navigator.showButterworth(); - } + public void gotToButterworth() { navigator.showButterworth(); } - public GrayScaleMatrix getImageGrayMatrix() { - return appState.getImage().toGrayScale(); - } + public void goToBasicMorphology(){ navigator.showBasicMorphology(); } - public void setMode(Mode mode) { - modeService.setMode(mode); - } + public GrayScaleMatrix getImageGrayMatrix() { return appState.getImage().toGrayScale(); } + + public void setMode(Mode mode) { modeService.setMode(mode); } } \ No newline at end of file diff --git a/src/main/java/presenters/morphology/MorphologyListener.java b/src/main/java/presenters/morphology/MorphologyListener.java new file mode 100644 index 0000000..9fc9db2 --- /dev/null +++ b/src/main/java/presenters/morphology/MorphologyListener.java @@ -0,0 +1,13 @@ +package presenters.morphology; + +import domain.image.Image; + +public interface MorphologyListener { + void onErosionChanged(Image image); + + void onDilationChanged(Image image); + + void onOpeningChanged(Image image); + + void onClosingChanged(Image image); +} diff --git a/src/main/java/presenters/morphology/MorphologyPresenter.java b/src/main/java/presenters/morphology/MorphologyPresenter.java new file mode 100644 index 0000000..c3a279c --- /dev/null +++ b/src/main/java/presenters/morphology/MorphologyPresenter.java @@ -0,0 +1,102 @@ +package presenters.morphology; + +import app.state.AppState; +import app.state.MorphologyState; +import com.google.inject.Inject; +import domain.image.Image; +import services.MorphologyService; +import ui.abstractions.IBasicMorphologyView; + +public class MorphologyPresenter implements MorphologyListener { + + private final AppState appState; + private final MorphologyState morphologyState; + private final MorphologyService morphologyService; + private IBasicMorphologyView view; + + @Inject + public MorphologyPresenter( + AppState appState, + MorphologyState morphologyState, + MorphologyService morphologyService + ) { + this.appState = appState; + this.morphologyState = morphologyState; + this.morphologyService = morphologyService; + } + + public void setView(IBasicMorphologyView view) { + this.view = view; + + if (view != null) { + morphologyState.subscribe(this); + } else { + morphologyState.unsubscribe(this); + } + } + + public void computeErosion(Integer maskSize) { + Image image = appState.getImage(); + + Image result = morphologyService.erosion(image, maskSize); + + morphologyState.setErosion(result); + } + + public void computeDilation(Integer maskSize) { + Image image = appState.getImage(); + + Image result = morphologyService.dilation(image, maskSize); + + morphologyState.setDilation(result); + } + + public void computeOpening(Integer maskSize) { + Image image = appState.getImage(); + + Image result = morphologyService.opening(image, maskSize); + + morphologyState.setOpening(result); + } + + public void computeClosing(Integer maskSize) { + Image image = appState.getImage(); + + Image result = morphologyService.closing(image, maskSize); + + morphologyState.setClosing(result); + } + + public void dispose() { + morphologyState.unsubscribe(this); + view = null; + } + + @Override + public void onErosionChanged(Image image) { + if (view != null) { + view.updateErosion(image); + } + } + + @Override + public void onDilationChanged(Image image) { + if (view != null) { + view.updateDilation(image); + } + } + + @Override + public void onOpeningChanged(Image image) { + if (view != null) { + view.updateOpening(image); + } + } + + @Override + public void onClosingChanged(Image image) { + if (view != null) { + view.updateClosing(image); + } + } +} \ No newline at end of file diff --git a/src/main/java/services/MorphologyService.java b/src/main/java/services/MorphologyService.java new file mode 100644 index 0000000..0594767 --- /dev/null +++ b/src/main/java/services/MorphologyService.java @@ -0,0 +1,43 @@ +package services; + +import domain.image.Image; +import domain.image.processing.morphology.BasicMorphology; +import domain.image.processing.morphology.ComplexMorphology; + +public class MorphologyService { + + public Image erosion(Image image, int size) { + return BasicMorphology.erosion(image, size).toImage(); + } + + public Image dilation(Image image, int size) { + return BasicMorphology.dilation(image, size).toImage(); + } + + public Image opening(Image image, int size) { + return BasicMorphology.opening(image, size).toImage(); + } + + public Image closing(Image image, int size) { + return BasicMorphology.closing(image, size).toImage(); + } + + public Image geodesicDilation(Image image, Image mask, int size) { + return ComplexMorphology + .geodesicDilation(image, mask, size) + .toImage(); + } + + public Image geodesicReconstruction(Image marker, Image mask, int size) { + return ComplexMorphology + .geodesicReconstruction(marker, mask, size) + .toImage(); + } + + public Image meanFilter(Image image, int size) { + return ComplexMorphology + .meanFilter(image, size) + .toImage(); + } + +} \ No newline at end of file diff --git a/src/main/java/ui/abstractions/IBasicMorphologyView.java b/src/main/java/ui/abstractions/IBasicMorphologyView.java new file mode 100644 index 0000000..5f77018 --- /dev/null +++ b/src/main/java/ui/abstractions/IBasicMorphologyView.java @@ -0,0 +1,10 @@ +package ui.abstractions; + +import domain.image.Image; + +public interface IBasicMorphologyView { + void updateErosion(Image image); + void updateDilation(Image image); + void updateOpening(Image image); + void updateClosing(Image image); +} diff --git a/src/main/java/ui/implementation/components/nav/NavBar.java b/src/main/java/ui/implementation/components/nav/NavBar.java index 13fa0e2..9816a79 100644 --- a/src/main/java/ui/implementation/components/nav/NavBar.java +++ b/src/main/java/ui/implementation/components/nav/NavBar.java @@ -34,6 +34,7 @@ public class NavBar extends JMenuBar implements INavBar { private JMenu fourierMenu; private JMenu linearMenu; private JMenu histogramMenu; + private JMenu morphologyMenu; private NavPresenter presenter; @@ -91,6 +92,11 @@ public class NavBar extends JMenuBar implements INavBar { ) ); + morphologyMenu = new Menu("Morphologie","/cp_51_p1.jpg", + new MenuItem("Elementaire", this::displayBasicMorphology), + new MenuItem("Complexe", this::displayComplexeMorphology) + ); + histogramMenu = new Menu("Histogramme","/report_48_hot.jpg", new MenuItem("Afficher", "/report_32_hot.jpg",this::displayHistogram) ); @@ -99,15 +105,16 @@ public class NavBar extends JMenuBar implements INavBar { this.add(drawingMenu); this.add(fourierMenu); this.add(linearMenu); + this.add(morphologyMenu); this.add(histogramMenu); drawingMenu.setEnabled(false); fourierMenu.setEnabled(false); linearMenu.setEnabled(false); + morphologyMenu.setEnabled(false); histogramMenu.setEnabled(false); } - //#################################################### private File chooseFile(){ @@ -173,6 +180,7 @@ public class NavBar extends JMenuBar implements INavBar { drawingMenu.setEnabled(true); fourierMenu.setEnabled(true); linearMenu.setEnabled(true); + morphologyMenu.setEnabled(true); histogramMenu.setEnabled(true); } @@ -181,6 +189,7 @@ public class NavBar extends JMenuBar implements INavBar { drawingMenu.setEnabled(true); fourierMenu.setEnabled(false); linearMenu.setEnabled(false); + morphologyMenu.setEnabled(false); histogramMenu.setEnabled(false); } @@ -243,4 +252,8 @@ public class NavBar extends JMenuBar implements INavBar { ); } + private void displayBasicMorphology(ActionEvent actionEvent) { presenter.goToBasicMorphology(); } + + private void displayComplexeMorphology(ActionEvent actionEvent) { + } } diff --git a/src/main/java/ui/implementation/views/morphology/BasicMorphologyView.java b/src/main/java/ui/implementation/views/morphology/BasicMorphologyView.java new file mode 100644 index 0000000..cb96585 --- /dev/null +++ b/src/main/java/ui/implementation/views/morphology/BasicMorphologyView.java @@ -0,0 +1,228 @@ +package ui.implementation.views.morphology; + +import domain.image.Image; +import jakarta.inject.Inject; +import presenters.morphology.MorphologyPresenter; +import ui.abstractions.AbstractView; +import ui.abstractions.IBasicMorphologyView; +import ui.implementation.components.controls.SaveButton; +import ui.implementation.components.image.TabbedImagePanel; + +import javax.swing.*; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DocumentFilter; +import java.awt.*; + +public class BasicMorphologyView extends AbstractView implements IBasicMorphologyView { + + private static final String EROSION_TAB_TITLE = "Erosion"; + private static final String DILATION_TAB_TITLE = "Dilatation"; + private static final String OPENING_TAB_TITLE = "Ouverture"; + private static final String CLOSING_TAB_TITLE = "Fermeture"; + + private final MorphologyPresenter presenter; + + private TabbedImagePanel modeTab; + private JTextField maskSizeField; + + @Inject + public BasicMorphologyView(MorphologyPresenter presenter) { + this.presenter = presenter; + } + + @Override + public void initComponents() { + modeTab = new TabbedImagePanel(); + + modeTab.addPreviewTab(EROSION_TAB_TITLE); + modeTab.addPreviewTab(DILATION_TAB_TITLE); + modeTab.addPreviewTab(OPENING_TAB_TITLE); + modeTab.addPreviewTab(CLOSING_TAB_TITLE); + + maskSizeField = createPositiveIntegerField(); + + JButton applyButton = new JButton("Appliquer"); + applyButton.addActionListener(e -> applyMorphology()); + + JButton saveButton = new SaveButton(); + + JPanel controlPanel = createControlPanel( + maskSizeField, + applyButton + ); + + JPanel rightPanel = new JPanel(); + + GroupLayout rightLayout = new GroupLayout(rightPanel); + rightPanel.setLayout(rightLayout); + rightLayout.setAutoCreateGaps(true); + rightLayout.setAutoCreateContainerGaps(true); + + rightLayout.setHorizontalGroup( + rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(controlPanel) + .addComponent(saveButton) + ); + + rightLayout.setVerticalGroup( + rightLayout.createSequentialGroup() + .addComponent(controlPanel) + .addComponent(saveButton) + ); + + JPanel mainPanel = new JPanel(); + + GroupLayout mainLayout = new GroupLayout(mainPanel); + mainPanel.setLayout(mainLayout); + mainLayout.setAutoCreateGaps(true); + mainLayout.setAutoCreateContainerGaps(true); + + mainLayout.setHorizontalGroup( + mainLayout.createSequentialGroup() + .addComponent(modeTab) + .addComponent(rightPanel) + ); + + mainLayout.setVerticalGroup( + mainLayout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(modeTab) + .addComponent(rightPanel) + ); + + setContentPane(mainPanel); + + setTitle("Traitement morphologique élémentaire"); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + pack(); + } + + @Override + public void initValues() { + this.modeTab.addChangeListener(e -> applyMorphology()); + presenter.setView(this); + applyMorphology(); + } + + private JPanel createControlPanel(JTextField sizeField, JButton applyButton) { + JPanel panel = new JPanel(); + + GroupLayout layout = new GroupLayout(panel); + panel.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + JLabel label = new JLabel("Taille du masque :"); + + layout.setHorizontalGroup( + layout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(label) + .addComponent( + sizeField, + GroupLayout.PREFERRED_SIZE, + 60, + GroupLayout.PREFERRED_SIZE + ) + .addComponent(applyButton) + ); + + layout.setVerticalGroup( + layout.createSequentialGroup() + .addComponent(label) + .addComponent( + sizeField, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE + ) + .addComponent(applyButton) + ); + + return panel; + } + + private JTextField createPositiveIntegerField() { + JTextField field = new JTextField("1", 5); + ((AbstractDocument) field.getDocument()).setDocumentFilter(new PositiveIntegerFilter()); + return field; + } + + private void applyMorphology() { + Integer size = getMaskSize(); + + if (size == null || size % 2 == 0) { + return; + } + + switch (modeTab.getSelectedIndex()) { + case 0 -> presenter.computeErosion(size); + case 1 -> presenter.computeDilation(size); + case 2 -> presenter.computeOpening(size); + case 3 -> presenter.computeClosing(size); + } + } + + private Integer getMaskSize() { + try { + int size = Integer.parseInt(maskSizeField.getText()); + + if (size <= 0) { + return null; + } + + return size; + } catch (NumberFormatException e) { + return null; + } + } + + public void updateErosion(Image image) { + modeTab.loadImage(EROSION_TAB_TITLE, image); + } + + public void updateDilation(Image image) { + modeTab.loadImage(DILATION_TAB_TITLE, image); + } + + public void updateOpening(Image image) { + modeTab.loadImage(OPENING_TAB_TITLE, image); + } + + public void updateClosing(Image image) { + modeTab.loadImage(CLOSING_TAB_TITLE, image); + } + + @Override + public void disposeCore() { + presenter.setView(null); + presenter.dispose(); + } + + private static class PositiveIntegerFilter extends DocumentFilter { + + @Override + public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr + ) throws BadLocationException { + if (isValid(fb.getDocument().getText(0, fb.getDocument().getLength()), offset, 0, string)) { + super.insertString(fb, offset, string, attr); + } + } + + @Override + public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { + if (isValid(fb.getDocument().getText(0, fb.getDocument().getLength()), offset, length, text)) { + super.replace(fb, offset, length, text, attrs); + } + } + + private boolean isValid(String current, int offset, int length, String replacement) { + String result = current.substring(0, offset) + replacement + current.substring(offset + length); + if (result.isEmpty()) { + return true; + } + + return result.matches("[1-9][0-9]*"); + } + } +} \ No newline at end of file