Добавление узла в тот же XML-файл на SD-карте под его корневым тегом в Android

1

Привет друзья У меня есть этот код, в котором я пишу xml файл на свою SD-карту:

public class SingleItemView extends Activity {

    File newxmlfile = new File(Environment.getExternalStorageDirectory() + "/testfinal.xml");

    TextView txtmdname,txtutcost,txtdesc,txtmodno;
    // Declare Variables 
    String mdname;
    String utcost;
    String desc;
    String modno;
    String flag;
    String toolscat;
    String tan;
    int number;
    String numberOfItems;
    ProgressDialog mProgressDialog;
    Bitmap bmImg = null;
    ImageView addToCartButton;
    final Context context = this;
    FileOutputStream fileos = null; 
    //String newxmlfile;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
        // Get the view from singleitemview.xml
        setContentView(R.layout.singleitemview);
        // Execute loadSingleView AsyncTask
        toolscat=getIntent().getExtras().getString("toolscat");
        tan=getIntent().getExtras().getString("tan");
        new loadSingleView().execute();

        if (newxmlfile.exists()){
            try{
                fileos = new FileOutputStream(newxmlfile, true);
            }catch(FileNotFoundException e){
                Log.e("FileNotFoundException", "can't create FileOutputStream");
            }
        } else {
            try{
                newxmlfile.createNewFile();
            }catch(IOException e){
                Log.e("IOException", "exception in createNewFile() method");
            }

            try{
                fileos = new FileOutputStream(newxmlfile);
            }catch(FileNotFoundException e){
                Log.e("FileNotFoundException", "can't create FileOutputStream");
            }
        }

        //xml 
        addToCartButton=(ImageView)findViewById(R.id.imageView1);
        addToCartButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) { 
                // get prompts.xml view
                LayoutInflater li = LayoutInflater.from(context);
                View promptsView = li.inflate(R.layout.promptdialog, null);

                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                        context);

                // set prompts.xml to alertdialog builder
                alertDialogBuilder.setView(promptsView);

                final EditText userInput = (EditText) promptsView
                        .findViewById(R.id.editTextDialogUserInput);

                // set dialog message
                alertDialogBuilder
                    .setCancelable(false)
                    .setPositiveButton("OK",
                      new DialogInterface.OnClickListener() {
                        @SuppressWarnings("null")
                        public void onClick(DialogInterface dialog,int id) {
                            numberOfItems=userInput.getText().toString();
                            number=Integer.parseInt(numberOfItems);
                            String utCostString=txtutcost.getText().toString();
                            float cost=Float.parseFloat(utCostString);
                            float totalCost=Float.parseFloat(utCostString)*number;
                            String tc = Float.toString(totalCost);
                            String codeText=txtmodno.getText().toString();

                            //Loop for writing xml

                            XmlSerializer serializer = Xml.newSerializer();
                            try {
                                serializer.setOutput(fileos, "UTF-8");

                                if(newxmlfile.length()==0) {
                                    serializer.startDocument(null, Boolean.valueOf(true));
                                    serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

                                    serializer.startTag(null, "root");
                                    serializer.startTag(null, "code");
                                    serializer.text(codeText);
                                    serializer.endTag(null, "code");

                                    serializer.startTag(null, "price");
                                    serializer.text(utCostString);
                                    serializer.endTag(null, "price");

                                    serializer.startTag(null, "quantity");
                                    serializer.text(numberOfItems);
                                    serializer.endTag(null, "quantity");

                                    serializer.startTag(null, "totalcost");
                                    serializer.text(tc);
                                    serializer.endTag(null, "totalcost");  
                                    serializer.endTag(null, "root");
                                } else {
                                    serializer.startTag(null, "root");
                                    serializer.startTag(null, "code");
                                    serializer.text(codeText);
                                    serializer.endTag(null, "code");

                                    serializer.startTag(null, "price");
                                    serializer.text(utCostString);
                                    serializer.endTag(null, "price");

                                    serializer.startTag(null, "quantity");
                                    serializer.text(numberOfItems);
                                    serializer.endTag(null, "quantity");

                                    serializer.startTag(null, "totalcost");
                                    serializer.text(tc);
                                    serializer.endTag(null, "totalcost"); 
                                    serializer.endTag(null, "root");
                                }

                                serializer.endDocument();
                                serializer.flush();
                                fileos.close();

                                Context context = getApplicationContext();
                                CharSequence text = "Save!";
                                int duration = Toast.LENGTH_SHORT;
                                Toast toast = Toast.makeText(context, text, duration);
                                toast.show();
                            } catch (Exception e) {
                                Log.e("Exception","error occurred while creating xml file");
                            }
                            //end of xml lopp
                        }
                      })
                    .setNegativeButton("Cancel",
                      new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,int id) {
                        dialog.cancel();
                        }
                      });

                // create alert dialog
                AlertDialog alertDialog = alertDialogBuilder.create();

                // show it
                alertDialog.show();
            }
        });
    }

    public class loadSingleView extends AsyncTask<String, String, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // Create a progressdialog
            mProgressDialog = new ProgressDialog(SingleItemView.this);
            // Set progressdialog title
            mProgressDialog.setTitle("Progress");
            // Set progressdialog message
            mProgressDialog.setMessage("Loading...");
            mProgressDialog.setIndeterminate(false);
            // Show progressdialog
            mProgressDialog.show();
        }

        @Override
        protected String doInBackground(String... args) {
            try {
                // Retrieve data from ListViewAdapter on click event
                Intent i = getIntent();
                // Get the result of rank
                mdname = i.getStringExtra("mdname");
                // Get the result of country
                utcost = i.getStringExtra("utcost");
                // Get the result of population
                desc = i.getStringExtra("desc");

                modno = i.getStringExtra("modno");
                // Get the result of flag
                flag = i.getStringExtra("flag");

                // Download the Image from the result URL given by flag
                URL url = new URL(flag);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setDoInput(true);
                conn.connect();
                InputStream is = conn.getInputStream();
                bmImg = BitmapFactory.decodeStream(is);
            } catch (IOException e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onPostExecute(String args) {
            // Locate the TextViews in singleitemview.xml
            txtmdname = (TextView) findViewById(R.id.mdname);
            txtutcost = (TextView) findViewById(R.id.utcost);
            txtdesc = (TextView) findViewById(R.id.desc);
            txtmodno = (TextView) findViewById(R.id.modno);
            // Locate the ImageView in singleitemview.xml
            ImageView imgflag = (ImageView) findViewById(R.id.flag);

            // Set results to the TextViews
            txtmdname.setText(mdname);
            txtutcost.setText(utcost);
            txtdesc.setText(desc);
            txtmodno.setText(modno);
            //    txtpopulation.setText(population);

            // Set results to the ImageView
            imgflag.setImageBitmap(bmImg);

            // Close the progressdialog
            mProgressDialog.dismiss();
        }
    }
}

Этот код дает мне такой вывод в моем XML файле:

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<root>
  <code>ACT445</code>
  <price>110.00</price>
  <quantity>4</quantity>
  <totalcost>440.0</totalcost>
</root><root><code>ACT430</code><price>110.00</price><quantity>5</quantity><totalcost>550.0</totalcost></root>

где я хочу, чтобы выходной XML файл был следующим:

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
    <root>
      <code>ACT445</code>
      <price>110.00</price>
      <quantity>4</quantity>
      <totalcost>440.0</totalcost>

      <code>ACT430</code>
      <price>110.00</price>
      <quantity>5</quantity>
      <totalcost>550.0</totalcost>
    </root>

Пожалуйста, предложите изменения, которые мне нужно сделать, чтобы получить результат так, как я хочу выше. Любая помощь будет оценена. Заранее спасибо.

***************** ОБНОВЛЕНИЕ *****************

После внесения изменений, как упоминал Сандстер. Теперь он может легко ввести две записи в xml без проблем. Но когда я пытаюсь написать третью запись, она будет написана с дополнительным символом, подобным этому:

<?xml version='1.0' standalone='yes' ?>
<root>
  <item>
    <code>ACT358</code>
    <price>110.00</price>
    <quantity>3</quantity>
    <totalcost>330.0</totalcost>
  </item>

<item>
  <code>ACT443</code>
  <price>110.00</price>
  <quantity>5</quantity>
  <totalcost>550.0</totalcost>
</item>
<
<item>
  <code>ACT388</code>
  <price>110.00</price>
  <quantity>4</quantity>
  <totalcost>440.0</totalcost>
</item>
</root>

Как вы видите после второго раза, появляется дополнительная <, которая записывается. Кажется, я не знаю, почему.

Мой новый код выглядит следующим образом:

public class SingleItemView extends Activity {

    File newxmlfile = new File(Environment.getExternalStorageDirectory() + "/testfinal.xml");

    TextView txtmdname,txtutcost,txtdesc,txtmodno;
    // Declare Variables 
    String mdname;
    String utcost;
    String desc;
    String modno;
    String flag;
    String toolscat;
    String tan;
    int number;
    String numberOfItems;
    ProgressDialog mProgressDialog;
    Bitmap bmImg = null;
    ImageView addToCartButton;
    final Context context = this;
    FileOutputStream fileos = null; 
    //String newxmlfile;



    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
        // Get the view from singleitemview.xml
        setContentView(R.layout.singleitemview);
        // Execute loadSingleView AsyncTask
        toolscat=getIntent().getExtras().getString("toolscat");
        tan=getIntent().getExtras().getString("tan");
        new loadSingleView().execute();

        if (!newxmlfile.exists()){
            try{
                newxmlfile.createNewFile();
            }catch(IOException e){
                Log.e("IOException", "exception in createNewFile() method");
            }
        }


        //xml 
        addToCartButton=(ImageView)findViewById(R.id.imageView1);
        addToCartButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) { 
                // get prompts.xml view
                LayoutInflater li = LayoutInflater.from(context);
                View promptsView = li.inflate(R.layout.promptdialog, null);

                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                        context);

                // set prompts.xml to alertdialog builder
                alertDialogBuilder.setView(promptsView);

                final EditText userInput = (EditText) promptsView
                        .findViewById(R.id.editTextDialogUserInput);

                // set dialog message
                alertDialogBuilder
                    .setCancelable(false)
                    .setPositiveButton("OK",
                      new DialogInterface.OnClickListener() {
                        @SuppressWarnings("null")
                        public void onClick(DialogInterface dialog,int id) {
                            numberOfItems=userInput.getText().toString();
                            number=Integer.parseInt(numberOfItems);
                            String utCostString=txtutcost.getText().toString();
                            float cost=Float.parseFloat(utCostString);
                            float totalCost=Float.parseFloat(utCostString)*number;
                            String tc = Float.toString(totalCost);
                            String codeText=txtmodno.getText().toString();

                            //Loop for writing xml

                            try {
                                updateFile(codeText, utCostString, tc);

                                Context context = getApplicationContext();
                                CharSequence text = "Save!";
                                int duration = Toast.LENGTH_SHORT;
                                Toast toast = Toast.makeText(context, text, duration);
                                toast.show();
                            } catch (Exception e) {
                                Log.e("Exception", "error occurred while creating xml file");
                            }
                            //end of xml lopp
                        }
                      })
                    .setNegativeButton("Cancel",
                      new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,int id) {
                        dialog.cancel();
                        }
                      });

                // create alert dialog
                AlertDialog alertDialog = alertDialogBuilder.create();

                // show it
                alertDialog.show();
            }
        });
    }

    //start of update function




    public class loadSingleView extends AsyncTask<String, String, String> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // Create a progressdialog
            mProgressDialog = new ProgressDialog(SingleItemView.this);
            // Set progressdialog title
            mProgressDialog.setTitle("Progress");
            // Set progressdialog message
            mProgressDialog.setMessage("Loading...");
            mProgressDialog.setIndeterminate(false);
            // Show progressdialog
            mProgressDialog.show();
        }

        @Override
        protected String doInBackground(String... args) {
            try {
                // Retrieve data from ListViewAdapter on click event
                Intent i = getIntent();
                // Get the result of rank
                mdname = i.getStringExtra("mdname");
                // Get the result of country
                utcost = i.getStringExtra("utcost");
                // Get the result of population
                desc = i.getStringExtra("desc");

                modno = i.getStringExtra("modno");
                // Get the result of flag
                flag = i.getStringExtra("flag");

                // Download the Image from the result URL given by flag
                URL url = new URL(flag);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setDoInput(true);
                conn.connect();
                InputStream is = conn.getInputStream();
                bmImg = BitmapFactory.decodeStream(is);
            } catch (IOException e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onPostExecute(String args) {
            // Locate the TextViews in singleitemview.xml
            txtmdname = (TextView) findViewById(R.id.mdname);
            txtutcost = (TextView) findViewById(R.id.utcost);
            txtdesc = (TextView) findViewById(R.id.desc);
            txtmodno = (TextView) findViewById(R.id.modno);
            // Locate the ImageView in singleitemview.xml
            ImageView imgflag = (ImageView) findViewById(R.id.flag);

            // Set results to the TextViews
            txtmdname.setText(mdname);
            txtutcost.setText(utcost);
            txtdesc.setText(desc);
            txtmodno.setText(modno);
            //    txtpopulation.setText(population);

            // Set results to the ImageView
            imgflag.setImageBitmap(bmImg);

            // Close the progressdialog
            mProgressDialog.dismiss();
        }
    }

    private void updateFile(final String codeText, final String utCostString, final String tc) 
    {
        RandomAccessFile randomAccessFile = null;
        String lastLine = null;
        boolean fileExists = true;

        if (newxmlfile.length() == 0) {
            fileExists = false;

            try {
                randomAccessFile = new RandomAccessFile(newxmlfile, "rw");
            } catch(FileNotFoundException e) {
                Log.e("FileNotFoundException", "can't create FileOutputStream");
            }
        } else {
            try {
                randomAccessFile = new RandomAccessFile(newxmlfile, "rw");
                randomAccessFile.seek(0);

                final Scanner scanner = new Scanner(newxmlfile);
                int lastLineOffset = 0;
                int lastLineLength = 0;

                while (scanner.hasNextLine()) {
                    // +1 is for end line symbol
                    lastLine = scanner.nextLine();
                    lastLineLength = lastLine.length() + 2;
                    lastLineOffset += lastLineLength;
                }

                // don't need last </root> line offset
                lastLineOffset -= lastLineLength;

                // got to string before last
                randomAccessFile.seek(lastLineOffset);
            } catch(FileNotFoundException e) {
                Log.e("FileNotFoundException", "can't create FileOutputStream");
            } catch (IOException e) {
                Log.e("IOException", "Failed to find last line");
            }
        }

        // Now random access file is positioned properly, we can append new xml data
        //we create a XmlSerializer in order to write xml data
        XmlSerializer serializer = Xml.newSerializer();

        if (randomAccessFile == null) {
            return;
        }

        try {
            final StringWriter writer = new StringWriter();

            serializer.setOutput(writer);

            if (!fileExists) {
                serializer.startDocument(null, true);
                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
                serializer.startTag(null, "root");
            } else {
                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
            }
            serializer.startTag(null, "item");
            serializer.startTag(null, "code");
            serializer.text(codeText);
            serializer.endTag(null, "code");

            serializer.startTag(null, "price");
            serializer.text(utCostString);
            serializer.endTag(null, "price");

            serializer.startTag(null, "quantity");
            serializer.text(numberOfItems);
            serializer.endTag(null, "quantity");

            serializer.startTag(null, "totalcost");
            serializer.text(tc);
            serializer.endTag(null, "totalcost");
            serializer.endTag(null, "item");
            if (!fileExists) {
                serializer.endTag(null, "root");
            }

            serializer.flush();

            if (lastLine != null) {
                serializer.endDocument();
                writer.append("\n");
                writer.append(lastLine);
            }

            // Add \n just for better output in console
            randomAccessFile.writeBytes(writer.toString() + "\n");
            randomAccessFile.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Спасибо заранее.

  • 0
    Я не очень знаком с API Android, но похоже, что XmlSerializer предназначен только для записи полного файла, а не для вставки элементов в определенные моменты. Вы должны быть в состоянии использовать DOM-подход DocumentBuilderFactory или SAX-подход для чтения существующего файла, добавления узла в правильной точке и записи результата, как в моем ответе, который вы уже видели.
  • 0
    Спасибо за ваш ответ. Я очень ценю это.
Показать ещё 4 комментария
Теги:
xml-serialization
fileoutputstream
appendchild

1 ответ

0

Проблема с вашим кодом заключается в том, что вы не добавляете новые данные должным образом.

Как описано в этом ответе в случае, если файл существует, вам нужно найти последнюю строку (в данном случае это </root>) и предоставить новые данные перед этим, поэтому он появится в том же XML-документе, но не в новом.

Ниже приведен код (я переместил часть изменения файла для отдельной функции и удалил все материалы, которые не связаны с вашей проблемой):

public class MyActivity extends Activity {

    private static final String TAG = "MyActivity";

    File newxmlfile = new File(Environment.getExternalStorageDirectory() + "/testfinal.xml");
    int number;
    String numberOfItems;
    ImageView addToCartButton;
    final Context context = this;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        Log.e(TAG, "File is " + Environment.getExternalStorageDirectory() + "/testfinal.xml");

        if (!newxmlfile.exists()){
            try{
                newxmlfile.createNewFile();
            }catch(IOException e){
                Log.e("IOException", "exception in createNewFile() method");
            }
        }

        //xml
        addToCartButton = (ImageView)findViewById(R.id.imageView1);
        addToCartButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // get prompts.xml view
                LayoutInflater li = LayoutInflater.from(context);
                View promptsView = li.inflate(R.layout.promptdialog, null);

                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                        context);

                if (promptsView == null) {
                    return;
                }

                // set prompts.xml to alertdialog builder
                alertDialogBuilder.setView(promptsView);

                final EditText userInput = (EditText) promptsView.findViewById(R.id.editTextDialogUserInput);

                // set dialog message
                alertDialogBuilder
                        .setCancelable(false)
                        .setPositiveButton("OK",
                                new DialogInterface.OnClickListener() {
                                    @Override
                                    public void onClick(DialogInterface dialog, int id) {
                                        if (userInput == null || userInput.getText() == null) {
                                            return;
                                        }

                                        numberOfItems = userInput.getText().toString();
                                        number = Integer.parseInt(numberOfItems);
                                        String utCostString = "200";//txtutcost.getText().toString();
                                        float cost = Float.parseFloat(utCostString);
                                        float totalCost = Float.parseFloat(utCostString) * number;
                                        String tc = Float.toString(totalCost);
                                        String codeText = "codeText";//txtmodno.getText().toString();

                                        //Loop for writing xml
                                        try {
                                            updateFile(codeText, utCostString, tc);

                                            Context context = getApplicationContext();
                                            CharSequence text = "Save!";
                                            int duration = Toast.LENGTH_SHORT;
                                            Toast toast = Toast.makeText(context, text, duration);
                                            toast.show();
                                        } catch (Exception e) {
                                            Log.e("Exception", "error occurred while creating xml file");
                                        }
                                        //end of xml lopp
                                    }
                                })
                        .setNegativeButton("Cancel",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int id) {
                                        dialog.cancel();
                                    }
                                });

                // create alert dialog
                AlertDialog alertDialog = alertDialogBuilder.create();

                // show it
                alertDialog.show();
            }
        });
    }

    /**
     * Appends new data to the file
     *
     * @param codeText
     * @param utCostString
     * @param tc
     */
    private void updateFile(final String codeText, final String utCostString, final String tc) throws IOException {
        RandomAccessFile randomAccessFile = null;
        String closingLine = null;
        boolean fileExists = true;

        if (newxmlfile.length() == 0) {
            fileExists = false;

            try {
                randomAccessFile = new RandomAccessFile(newxmlfile, "rw");
            } catch(FileNotFoundException e) {
                Log.e("FileNotFoundException", "can't create FileOutputStream");
            }
        } else {
            try {
                randomAccessFile = new RandomAccessFile(newxmlfile, "rw");
                randomAccessFile.seek(0);

                String lastLine;
                long lastLineOffset = 0;
                int lastLineLength = 0;

                lastLine = randomAccessFile.readLine();

                while (lastLine != null) {
                    // +1 is for end line symbol
                    lastLineLength = lastLine.length();
                    lastLineOffset = randomAccessFile.getFilePointer();

                    closingLine = lastLine;
                    lastLine = randomAccessFile.readLine();
                }

                lastLineOffset -= lastLineLength;
                // got to string before last
                randomAccessFile.seek(lastLineOffset);
            } catch(FileNotFoundException e) {
                Log.e("FileNotFoundException", "can't create FileOutputStream");
            } catch (IOException e) {
                Log.e("IOException", "Failed to find last line");
            }
        }

        // Now random access file is positioned properly, we can append new xml data
        //we create a XmlSerializer in order to write xml data
        XmlSerializer serializer = Xml.newSerializer();

        if (randomAccessFile == null) {
            return;
        }

        try {
            final StringWriter writer = new StringWriter();

            serializer.setOutput(writer);

            if (!fileExists) {
                serializer.startDocument(null, true);
                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
                serializer.startTag(null, "root");
            } else {
                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
            }

            serializer.startTag(null, "code");
            serializer.text(codeText);
            serializer.endTag(null, "code");

            serializer.startTag(null, "price");
            serializer.text(utCostString);
            serializer.endTag(null, "price");

            serializer.startTag(null, "quantity");
            serializer.text(numberOfItems);
            serializer.endTag(null, "quantity");

            serializer.startTag(null, "totalcost");
            serializer.text(tc);
            serializer.endTag(null, "totalcost");

            if (!fileExists) {
                serializer.endTag(null, "root");
            }

            serializer.flush();

            if (closingLine != null) {
                serializer.endDocument();
                writer.append("\n");
                writer.append(closingLine);
            }

            // Add \n just for better output in console
            randomAccessFile.writeBytes(writer.toString());
            randomAccessFile.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            randomAccessFile.close();
        }
    }
}
  • 0
    спасибо большое, я постараюсь, если это работает, и вернусь, чтобы принять ответ. Благодарю.
  • 0
    Благодаря @sandrstar с вашим кодом, теперь я могу без проблем ввести две записи, но 3-я и 4-я записи, которые следуют за дополнительными символами, пишутся, а xml не формируется должным образом. Пожалуйста, посмотрите на обновленный код в моем вопросе. Большое спасибо за вашу помощь заранее.
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню