class AbstractBorderRenderer implements Border.BorderRenderer {
        private final BorderStyle borderStyle;

        protected AbstractBorderRenderer(BorderStyle borderStyle) {
            this.borderStyle = borderStyle;
        }

        @Override
        public TerminalSize getPreferredSize(Border component) {<FILL_FUNCTION_BODY>}

        @Override
        public TerminalPosition getWrappedComponentTopLeftOffset() {
            return TerminalPosition.OFFSET_1x1;
        }

        @Override
        public TerminalSize getWrappedComponentSize(TerminalSize borderSize) {
            return borderSize
                    .withRelativeColumns(-Math.min(2, borderSize.getColumns()))
                    .withRelativeRows(-Math.min(2, borderSize.getRows()));
        }

        @Override
        public void drawComponent(TextGUIGraphics graphics, Border component) {
            StandardBorder border = (StandardBorder)component;
            Component wrappedComponent = border.getComponent();
            if(wrappedComponent == null) {
                return;
            }
            TerminalSize drawableArea = graphics.getSize();

            char horizontalLine = getHorizontalLine(component.getTheme());
            char verticalLine = getVerticalLine(component.getTheme());
            char bottomLeftCorner = getBottomLeftCorner(component.getTheme());
            char topLeftCorner = getTopLeftCorner(component.getTheme());
            char bottomRightCorner = getBottomRightCorner(component.getTheme());
            char topRightCorner = getTopRightCorner(component.getTheme());
            char titleLeft = getTitleLeft(component.getTheme());
            char titleRight = getTitleRight(component.getTheme());

            ThemeDefinition themeDefinition = component.getTheme().getDefinition(AbstractBorder.class);
            if(borderStyle == BorderStyle.Bevel) {
                graphics.applyThemeStyle(themeDefinition.getPreLight());
            }
            else {
                graphics.applyThemeStyle(themeDefinition.getNormal());
            }
            graphics.setCharacter(0, drawableArea.getRows() - 1, bottomLeftCorner);
            if(drawableArea.getRows() > 2) {
                graphics.drawLine(new TerminalPosition(0, drawableArea.getRows() - 2), new TerminalPosition(0, 1), verticalLine);
            }
            graphics.setCharacter(0, 0, topLeftCorner);
            if(drawableArea.getColumns() > 2) {
                graphics.drawLine(new TerminalPosition(1, 0), new TerminalPosition(drawableArea.getColumns() - 2, 0), horizontalLine);
            }

            if(borderStyle == BorderStyle.ReverseBevel) {
                graphics.applyThemeStyle(themeDefinition.getPreLight());
            }
            else {
                graphics.applyThemeStyle(themeDefinition.getNormal());
            }
            graphics.setCharacter(drawableArea.getColumns() - 1, 0, topRightCorner);
            if(drawableArea.getRows() > 2) {
                graphics.drawLine(new TerminalPosition(drawableArea.getColumns() - 1, 1),
                        new TerminalPosition(drawableArea.getColumns() - 1, drawableArea.getRows() - 2),
                        verticalLine);
            }
            graphics.setCharacter(drawableArea.getColumns() - 1, drawableArea.getRows() - 1, bottomRightCorner);
            if(drawableArea.getColumns() > 2) {
                graphics.drawLine(new TerminalPosition(1, drawableArea.getRows() - 1),
                        new TerminalPosition(drawableArea.getColumns() - 2, drawableArea.getRows() - 1),
                        horizontalLine);
            }


            if(border.getTitle() != null && !border.getTitle().isEmpty() &&
                    drawableArea.getColumns() >= TerminalTextUtils.getColumnWidth(border.getTitle()) + 4) {
                graphics.applyThemeStyle(themeDefinition.getActive());
                graphics.putString(2, 0, border.getTitle());

                if(borderStyle == BorderStyle.Bevel) {
                    graphics.applyThemeStyle(themeDefinition.getPreLight());
                }
                else {
                    graphics.applyThemeStyle(themeDefinition.getNormal());
                }
                graphics.setCharacter(1, 0, titleLeft);
                graphics.setCharacter(2 + TerminalTextUtils.getColumnWidth(border.getTitle()), 0, titleRight);
            }

            wrappedComponent.draw(graphics.newTextGraphics(getWrappedComponentTopLeftOffset(), getWrappedComponentSize(drawableArea)));
            joinLinesWithFrame(graphics);
        }

        protected abstract char getHorizontalLine(Theme theme);
        protected abstract char getVerticalLine(Theme theme);
        protected abstract char getBottomLeftCorner(Theme theme);
        protected abstract char getTopLeftCorner(Theme theme);
        protected abstract char getBottomRightCorner(Theme theme);
        protected abstract char getTopRightCorner(Theme theme);
        protected abstract char getTitleLeft(Theme theme);
        protected abstract char getTitleRight(Theme theme);
    }

            StandardBorder border = (StandardBorder)component;
            Component wrappedComponent = border.getComponent();
            TerminalSize preferredSize;
            if (wrappedComponent == null) {
                preferredSize = TerminalSize.ZERO;
            } else {
                preferredSize = wrappedComponent.getPreferredSize();
            }
            preferredSize = preferredSize.withRelativeColumns(2).withRelativeRows(2);
            String borderTitle = border.getTitle();
            return preferredSize.max(new TerminalSize((borderTitle.isEmpty() ? 2 : TerminalTextUtils.getColumnWidth(borderTitle) + 4), 2));
        
<no_super_class>