Java tutorial
/* * ================================================================================= * Copyright (C) 2013 Martin Albedinsky [Wolf-ITechnologies] * ================================================================================= * Licensed under the Apache License, Version 2.0 or later (further "License" only); * --------------------------------------------------------------------------------- * You may use this file only in compliance with the License. More details and copy * of this License you may obtain at * * http://www.apache.org/licenses/LICENSE-2.0 * * You can redistribute, modify or publish any part of the code written in this * file but as it is described in the License, the software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES or CONDITIONS OF * ANY KIND. * * See the License for the specific language governing permissions and limitations * under the License. * ================================================================================= */ package com.wit.and.dialog.internal.xml; import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.support.v4.app.DialogFragment; import android.util.AndroidRuntimeException; import android.util.Log; import com.wit.and.dialog.Dialog; import com.wit.and.dialog.xml.XmlDialog; import com.wit.and.dialog.xml.XmlDialogParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.security.InvalidParameterException; import java.util.HashMap; /** * <h4>Class Overview</h4> * <p> * </p> * <h5>Supported dialog Root tags:</h5> * <h6>Dialog</h6> * <p> * Parsed to instance of {@link com.wit.and.dialog.Dialog}. * </p> * <h6>InfoDialog</h6> * <p> * Parsed to instance of {@link com.wit.and.dialog.Dialog} with default info icon * and single button. * </p> * <p> * Should contains only title and message text and can overrides text value of neutral * button ({@link com.wit.and.dialog.R.attr#dialogButtonNeutral}). * </p> * <h6>AlertDialog</h6> * <p> * Parsed to instance of {@link com.wit.and.dialog.Dialog} with default alert icon * and two buttons. * </p> * <p> * Should contains only title and message text and can overrides text value of negative * ({@link com.wit.and.dialog.R.attr#dialogButtonNegative}) and positive * ({@link com.wit.and.dialog.R.attr#dialogButtonPositive}) button. * </p> * <h6>ErrorDialog</h6> * <p> * Parsed to instance of {@link com.wit.and.dialog.Dialog} with default error icon * and single button. * </p> * <p> * Should contains only title and message text and can overrides text value of neutral * button ({@link com.wit.and.dialog.R.attr#dialogButtonNeutral}). * </p> * <h6>WebDialog</h6> * <p> * </p> * <h6>LoadingDialog</h6> * <p> * </p> * <h6>ProgressDialog</h6> * <p> * </p> * <h6>LoginDialog</h6> * <p> * </p> * * @author Martin Albedinsky * @see Dialog * @see com.wit.and.dialog.manage.DialogOptions * @see com.wit.and.dialog.manage.DialogFactory */ public final class XmlDialogInflater { /** * Constants ============================= */ /** * Log TAG. */ private static final String TAG = XmlDialogInflater.class.getSimpleName(); /** * Indicates if debug private output trough log-cat is enabled. */ private static final boolean DEBUG = false; /** * Indicates if logging for user output trough log-cat is enabled. */ private static final boolean USER_LOG = true; private static final String DIALOGS_SET_ROOT_TAG = "Dialogs"; private static final int MAX_CACHED_PARSERS = 10; /** * Enums ================================= */ /** * Static members ======================== */ /** * */ private static final HashMap<String, Class<? extends XmlDialogParser>> PARSERS = new HashMap<String, Class<? extends XmlDialogParser>>(); // Register default parsers. static { PARSERS.put(XmlDialog.XML_TAG, XmlDialog.class); PARSERS.put(XmlDialog.XmlInfoDialog.XML_TAG, XmlDialog.XmlInfoDialog.class); PARSERS.put(XmlDialog.XmlAlertDialog.XML_TAG, XmlDialog.XmlAlertDialog.class); PARSERS.put(XmlDialog.XmlErrorDialog.XML_TAG, XmlDialog.XmlErrorDialog.class); PARSERS.put(XmlProgressDialog.XML_TAG, XmlProgressDialog.class); PARSERS.put(XmlProgressDialog.XmlLoadingDialog.XML_TAG, XmlProgressDialog.XmlLoadingDialog.class); PARSERS.put(XmlWebDialog.XML_TAG, XmlWebDialog.class); PARSERS.put(XmlEditDialog.XML_TAG, XmlEditDialog.class); PARSERS.put(XmlLoginDialog.XML_TAG, XmlLoginDialog.class); } /** * */ private static final HashMap<String, XmlDialogParser> CACHED_PARSERS = new HashMap<String, XmlDialogParser>(); /** * Members =============================== */ /** * Resources to bind dialog options. */ private final Resources mResources; /** * Listeners ----------------------------- */ /** * Arrays -------------------------------- */ /** * Booleans ------------------------------ */ /** * Constructors ========================== */ /** * <p> * Constructs parser manager for XML dialogs. * </p> * * @param context Current context. */ public XmlDialogInflater(Context context) { this.mResources = context.getResources(); } /** * Methods =============================== */ /** * Public -------------------------------- */ /** * <p> * Registers XML dialog parser. Note, that parser will be registered only in case, that * there is no such a parser already registered for the <var>dialogTag</var>. * </p> * * @param parserClass Class of XML dialog parser to register. * @param dialogTag Tag of XML dialog for which should be the given parser registered. */ public void registerParser(Class<? extends XmlDialogParser> parserClass, String dialogTag) { if (!PARSERS.containsKey(dialogTag)) { PARSERS.put(dialogTag, parserClass); } else { if (USER_LOG) { Log.i(TAG, "Xml dialog parser for dialog tag(" + dialogTag + ") is already registered."); } } } /** * <p> * Inflates dialog fragment instance form the given XML dialogs set resource using one * of the registered XML dialog parsers. * </p> * * @param dialogID Id of dialog in the given <var>xmlSetRes</var> to inflate. * @param xmlSetRes Resource of XML file placed in the application resources. All XML dialogs in this XML file * must be placed inside <b><Dialogs></Dialogs></b> root tag. * @return New instance of {@link DialogFragment} or its derived classes, depends on the XML dialog * root tag. * @throws XmlDialogInflater.UnsupportedXmlDialogTagException * @throws java.security.InvalidParameterException * @throws java.lang.IllegalStateException * @see #registerParser(Class, String) */ public DialogFragment inflateDialog(int dialogID, int xmlSetRes) { XmlResourceParser xmlParser = mResources.getXml(xmlSetRes); if (xmlParser == null) return null; DialogFragment dialog = null; long time; if (DEBUG) { time = System.currentTimeMillis(); } try { int xmlEvent; boolean dialogsTagResolved = false; while ((xmlEvent = xmlParser.getEventType()) != XmlResourceParser.END_DOCUMENT) { switch (xmlEvent) { case XmlResourceParser.START_DOCUMENT: break; case XmlResourceParser.START_TAG: String tag = xmlParser.getName(); if (!dialogsTagResolved) { // Check valid root tag. if (!tag.equals(DIALOGS_SET_ROOT_TAG)) { throw new UnsupportedXmlDialogTagException( "Only 'Dialogs' tag is allowed as root tag of XML dialogs set."); } dialogsTagResolved = true; } else { // Check for empty dialog. if (xmlParser.getAttributeCount() == 0) { throw new IllegalStateException("Empty dialogs are not allowed."); } // Find the dialog with requested id. // Note, that first attribute of each dialog must be its "id" to preserve // fast parsing of xml file. final int attr = xmlParser.getAttributeNameResource(0); if (attr != android.R.attr.id) { throw new InvalidParameterException( "First attribute of XML dialog must be always 'android:id'."); } // Finally check the dialog id. final int id = xmlParser.getAttributeResourceValue(0, 0); if (dialogID == id) { dialog = parseDialogInner(xmlParser); } } break; } if (dialog != null) { break; } xmlParser.next(); } } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (DEBUG) { Log.d(TAG, "Parsing of XML dialog from dialogs set in " + Long.toString(System.currentTimeMillis() - time) + "ms"); } return dialog; } /** * <p> * Inflates dialog fragment instance form the given XML resource using one * of the registered XML dialog parsers. * </p> * * @param xmlRes Resource of XML file placed in the application resources. File should contains only * one dialog root tag. * @return New instance of {@link DialogFragment} or its derived classes, depends on the XML dialog * root tag. * @throws XmlDialogInflater.UnsupportedXmlDialogTagException * @see #registerParser(Class, String) */ public DialogFragment inflateDialog(int xmlRes) { XmlResourceParser xmlParser = mResources.getXml(xmlRes); if (xmlParser == null) return null; DialogFragment dialog = null; try { int xmlEvent; while ((xmlEvent = xmlParser.getEventType()) != XmlResourceParser.END_DOCUMENT) { switch (xmlEvent) { case XmlResourceParser.START_DOCUMENT: break; case XmlResourceParser.START_TAG: dialog = parseDialogInner(xmlParser); break; } if (dialog != null) { break; } xmlParser.next(); } } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return dialog; } /** * Getters + Setters --------------------- */ /** * Protected ----------------------------- */ /** * Private ------------------------------- */ /** * Parses dialog instance form the given xml parser depends on the current parser name. * * @param xmlParser Xml parser with currently being parsed xml file. * @return New instance of dialog or <code>null</code> if some error occur. * @throws XmlDialogInflater.UnsupportedXmlDialogTagException */ private DialogFragment parseDialogInner(XmlResourceParser xmlParser) { final String rootTag = xmlParser.getName(); DialogFragment dialog; // Get the parser for root tag. XmlDialogParser parser = getParser(rootTag); if (parser != null) { // Prepare parser. parser.prepare(); // Parse dialog. dialog = parser.parseXmlDialog(xmlParser); } else { throw new UnsupportedXmlDialogTagException( "Failed to parse XML dialog. Unknown XML dialog root tag('" + rootTag + "')."); } return dialog; } /** * Returns parser for the requested XML dialog root tag. * * @param rootTag XML dialog root tag. * @return Xml dialog parser or <code>null</code> if there is no parser registered for * the requested root tag. */ private XmlDialogParser getParser(String rootTag) { // Check if there is cached parser for requested tag. XmlDialogParser parser = CACHED_PARSERS.get(rootTag); if (parser == null) { final Class<? extends XmlDialogParser> parserClass = PARSERS.get(rootTag); if (parserClass != null) { try { // Create new parser. parser = parserClass.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } finally { if (parser != null) { // Set up and add parser into cache. parser.dispatchSetUp(mResources); // Clear cached parsers if needed. if (CACHED_PARSERS.size() > MAX_CACHED_PARSERS) { CACHED_PARSERS.clear(); } CACHED_PARSERS.put(rootTag, parser); } } } } return parser; } /** * Abstract methods ---------------------- */ /** * Inner classes ========================= */ /** * <h4>Class Overview</h4> * <p> * Exception which is thrown when there is unsupported XML (dialog) tag inside parsed XML file with XML dialog/-s. * </p> */ public static class UnsupportedXmlDialogTagException extends AndroidRuntimeException { public UnsupportedXmlDialogTagException(String name) { super(name); } } /** * Interface ============================= */ }