*  @param subcat do you want to return members of sub-categories also? (default: false)
     *  @param ns a list of namespaces to filter by, empty = all namespaces.
     *  @return a String[] containing page titles of members of the category
     *  @throws IOException if a network error occurs
     *  @since 0.03 
     */
    public String[] getCategoryMembers(String name, boolean subcat, int... ns) throws IOException
    {
        name = name.replaceFirst("^(Category|" + namespaceIdentifier(CATEGORY_NAMESPACE) + "):", "");
        StringBuilder url = new StringBuilder(query);
        url.append("list=categorymembers&cmprop=title&cmlimit=max&cmtitle=Category:");
        url.append(URLEncoder.encode(normalize(name), "UTF-8"));
        boolean nocat = true;
        if (subcat && ns.length != 0)
        {
            for (int i = 0; nocat && i < ns.length; i++)
                nocat = (ns[i] != CATEGORY_NAMESPACE);
            if (nocat)
            {
                int[] temp = Arrays.copyOf(ns, ns.length + 1);
                temp[ns.length] = CATEGORY_NAMESPACE;
                constructNamespaceString(url, "cm", temp);
            }
            else
                constructNamespaceString(url, "cm", ns);
        }
        else
            constructNamespaceString(url, "cm", ns);
        ArrayList<String> members = new ArrayList<String>();
        String next = "";
        do
        {
            if (!next.isEmpty())
                next = "&cmcontinue=" + URLEncoder.encode(next, "UTF-8");
            String line = fetch(url.toString() + next, "getCategoryMembers");

            // parse cmcontinue if it is there
            if (line.contains("cmcontinue"))
                next = parseAttribute(line, "cmcontinue", 0);
            else
                next = null;

            // xml form: <cm pageid="24958584" ns="3" title="User talk:86.29.138.185" />
            for (int x = line.indexOf("<cm "); x > 0; x = line.indexOf("<cm ", ++x))
            {
                String member = decode(parseAttribute(line, "title", x));
                
                // fetch subcategories
                boolean iscat = namespace(member) == CATEGORY_NAMESPACE;
                if (subcat && iscat)
                    members.addAll(Arrays.asList(getCategoryMembers(member, true, ns)));
                
                // ignore this item if we requested subcat but not CATEGORY_NAMESPACE
                if (!subcat || !nocat || !iscat)
                    members.add(member);
            }
        }
        while (next != null);
        
        int size = members.size();
        log(Level.INFO, "Successfully retrieved contents of Category:" + name + " (" + size + " items)", "getCategoryMembers");
        return members.toArray(new String[size]);
    }