Parser.java 9.55 KB
package com.upc.pbe.upcnews;

import java.net.MalformedURLException;
import java.util.ArrayList;

public class Parser
{	
	private static final String STARTWORD = "#EXTM3U";
	private int fileType; //indicates if segment list or media list
	private int currentLine;
	private int currentSegment; //Cada segmento de cada calidad distinta
	private int currentList; //Indice de cada recurso distinto
	private boolean expectSegment = false;
	private boolean expectList = false;
	private Descarrega download;
	Parser p = new Parser();
	
	public Parser()
	{
		currentLine = 0;
		currentSegment = 0;
		currentList = 0;
		fileType = -1;
		/*
		 * -1 indicates UNDETERMINED
		 * 0 indicates SEGMENTS
		 * 1 indicates MEDIA
		 */
		download = new Descarrega();
	}
	
	public ArrayList<ParentList> parseFile(String file) throws ErrorException, WarningException, InfoException
	{
		ArrayList<ParentList> lists = new ArrayList<ParentList>();
		lists.add(new ParentList(""));
		lists.get(0).getLists().add(new List(-1));
		String[] lines = file.split("\n");
		for(int i = 0; i < lines.length; i++)
		{
			
			parseLine(lines[i],lists);
		}
		/* 
		 * Esto contiene una lista de RECURSOS,
		 * que a su vez contiene una lista de CALIDADES.
		 * Si no te gusta mis VERSOS,
		 * te mando a la mierda sin SUTILIDADES.
		 * 
		 * -- Imanol, hasta las cejas de cafeína.
		 * 
		 * PD: En el último nivel de profundidad hay una lista de segmentos (debajo de calidades).
		 * Allí está el auténtico tesoro de Narnia.
		 */
		for(int i = 0; i < lists.size(); i++)
		{
			sortQuality(lists.get(i));
		}
		return lists; 
	}
	
	private int searchID(String ID, ArrayList<ParentList> lists)
	{
		for(int i = 0; i < lists.size(); i++)
		{
			if(lists.get(i).getID().equals(ID))
			{
				return i;
			}
		}
		return -1;
	}
	
	private void sortQuality(ParentList ppls)
	{
		ArrayList<List> lists = ppls.getLists();
		if(lists.get(0).getQuality() == -1)
		{
			/*
			 * Se trata de ext-x-media's y estos no van por calidad
			 */
			return;
		}
		//BUBBLE-SORT!! (Me da un poco de verguenza, pero no quiero ponerme con quicksort...
		while(true)
		{
			boolean sorted = true;
			int i = 0;
			do
			{
				if(lists.get(i).getQuality() < lists.get(i+1).getQuality())
				{
					sorted = false;
					List aux = lists.get(i);
					lists.set(i, lists.get(i+1));
					lists.set(i+1, aux);
				}
				
			}while(++i != lists.size());
			if(sorted)
			{
				break;
			}
		}

	}
	
	public void parseLine(String line, ArrayList<ParentList> lists) throws ErrorException, WarningException, InfoException
	{
		if(line.isEmpty())
		{
			currentLine++;
			return;
		}
		else
		{
			ParentList ppls = lists.get(currentList); //VIVA HONDURAS!!!
			List pls = ppls.getLists().get(ppls.getCurrentQuality());
			if(!pls.getValidated())
			{
				if(line.equals(STARTWORD))
				{
					pls.setValidated(true);
				}
				else
				{
					throw new ErrorException("Playlist is not valid");
				}
			}
			else
			{
				if(line.charAt(0) == '#')
				{
					if(line.substring(0,4).equals("#EXT"))
					{
						String[] extTag = line.split(":");
						if(extTag[0].equals("#EXT-X-MEDIA-SEQUENCE"))
						{
							pls.setSequence(Integer.parseInt(extTag[1]));
						}
						else if(extTag[0].equals("#EXT-X-TARGETDURATION"))
						{
							pls.setMaxDuration(Integer.parseInt(extTag[1]));
						}						
						else if(extTag[0].equals("#EXTINF"))
						{
							if(fileType == -1)
							{
								fileType = 0;
							}
							else if(fileType == 1)
							{
								throw new ErrorException("Invalid file, should contain Lists, " +
										                 "but segments were found.");
							}
							if(expectSegment)
							{
								throw new ErrorException("Expected segment URI, got a different" +
														 "segment declaration");
							}
							String[] args = extTag[1].split(",");
							int duration = Integer.parseInt(args[0]);
							if (duration > pls.getMaxDuration())
							{
								throw new ErrorException("Segment " + currentSegment + " on line " + currentLine 
										+" exceeds max duration");
							}
							Segment s = new Segment(duration);
							s.setName(args[1]);
							pls.getSegments().add(s);
							expectSegment = true;
						}
						
						else if(extTag[0].equals("#EXT-X-STREAM-INF"))
						{
							if(fileType == -1)
							{
								fileType = 1;
							}
							else if(fileType == 0)
							{
								throw new ErrorException("Invalid file, should contain Segments, " +
										                 "but lists were found.");
							}
							expectList = true;
							String programID = "";
							int bandwidth = -1;
							String[] arguments = extTag[1].split(",");
							for(int i = 0; i < arguments.length; i++)
							{
								String[] argument = arguments[i].split("=");
								if(argument[0].equals("PROGRAM-ID"))
								{
									programID = argument[1];
								}
								else if(argument[0].equals("BANDWIDTH"))
								{
									bandwidth = Integer.parseInt(argument[1]);
								}
							}
							if(programID.equals("")|| bandwidth == -1)
							{
								throw new ErrorException("Playlist on line " + currentLine + 
														 " has missing arguments");
							}
							if(!programID.equals(ppls.getID()))
							{
								if(ppls.getID().equals(""))
								{
									/*
									 * Este caso se da cuando no se ha creado ninguna lista.
									 * Hay que sustituir la por defecto por una lista real.
									 */
									lists.add(0,new ParentList(programID));
								}
								else
								{
									currentList = searchID(ppls.getID(), lists);
									if(currentList == -1)
									{
										lists.add(new ParentList(programID));
										currentList = lists.size()+1;
										/*
										 * ppls y pls se actualizan en la siguiente linea procesada
										 */
									}
									else
									{
										ppls = lists.get(currentList);
										ppls.getLists().add(new List(bandwidth));
									}
								}
							}
							else
							{
								ppls.getLists().add(new List(bandwidth));
							}
						}
						else if(extTag[0].equals("#EXT-X-MEDIA"))
						{
							if(fileType == -1)
							{
								fileType = 1;
							}
							else if(fileType == 0)
							{
								throw new ErrorException("Invalid file, should contain Segments, " +
										                 "but lists were found.");
							}
							String Type = "";
							String Name = "";
							String GroupID = "";
							String URI = "";
							boolean Default = false;
							String[] arguments = extTag[1].split(",");
							for(int i = 0; i < arguments.length; i++)
							{
								String[] argument = arguments[i].split("=");
								if(argument[0].equals("NAME"))
								{
									Name = argument[1];
								}
								else if(argument[0].equals("TYPE"))
								{
									Type = argument[1];
								}
								else if(argument[0].equals("GROUP-ID"))
								{
									GroupID = argument[1];
								}
								else if(argument[0].equals("DEFAULT"))
								{
									if(argument[1].equals("YES"))
									{
										Default = true;
									}
									else if(argument[1].equals("NO"))
									{
										Default = true;
									}
									else
									{
										throw new ErrorException("Invalid value for argument DEFAULT on line " +
												                 currentLine);
									}
								}
								else if(argument[0].equals("URI"))
								{
									URI = argument[1];
								}
							}
							if(Type.equals("") || Name.equals("") || URI.equals("") || GroupID.equals(""))
							{
								throw new ErrorException("Playlist on line " + currentLine + 
														 " has missing arguments");
							}
							ParentList pl = new ParentList(GroupID);
							pl.setDefault(Default);
							pl.setName(Name);
							pl.setType(Type);
							p.parseFile(download.doInBackground(URI));
							/*
							 * Procesar URI y descargar listas recursivamente y añadir a la lista actual.
							 * 
							 * TO-DO: Necesito la funcion de Marc
							 */
							List l = new List(-1);
							if(!GroupID.equals(ppls.getID()))
							{
								if(ppls.getID().equals(""))
								{
									/*
									 * Este caso se da cuando no se ha creado ninguna lista.
									 * Hay que sustituir la por defecto por una lista real.
									 */
									lists.add(0,pl);
								}
								else
								{

									currentList = searchID(ppls.getID(), lists);
									if(currentList == -1)
									{
										lists.add(pl);
										currentList = lists.size()+1;
									}
									else
									{
										ppls = lists.get(currentList);
										ppls.getLists().add(l);
									}
								}
							}
							else
							{
								ppls.getLists().add(l);
							}
						}
						
						else if(extTag[0].equals("#EXT-X-ENDLIST"))
						{
							if(expectSegment || expectList)
							{
								throw new ErrorException("Unexpected end of list!");
							}
							//END REACHED, IGNORE AND LET THE FILE REACH EOF						
						}
						
						else
						{
							currentLine++;
							throw new WarningException("Tag not implemented: " + extTag[0]);
						}
					}
					else
					{
						String comment = line.substring(1);
						throw new InfoException("Comment on line " + currentLine++ + " " + comment);
					}
				}
				else
				{
					if(expectSegment)
					{
						expectSegment = false;
						try
						{
							pls.getSegments().get(currentSegment).setURL(line);
						}
						catch (MalformedURLException e)
						{
							throw new ErrorException(e.getMessage());
						}
						currentSegment++;
					}
					else if(expectList)
					{
						expectList = false;
						p.parseFile(download.doInBackground(line));
					}
					else
					{
							throw new ErrorException("ERROR: Unexpected string " + line);
					}
				}
			}
			currentLine++;
		}	
	}
}